├── ai4j └── src │ ├── main │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ ├── io.github.lnyocly.ai4j.network.DispatcherProvider │ │ │ └── io.github.lnyocly.ai4j.network.ConnectionPoolProvider │ └── java │ │ └── io │ │ └── github │ │ └── lnyocly │ │ └── ai4j │ │ ├── exception │ │ ├── CommonException.java │ │ ├── chain │ │ │ ├── IErrorHandler.java │ │ │ ├── impl │ │ │ │ ├── UnknownErrorHandler.java │ │ │ │ ├── OpenAiErrorHandler.java │ │ │ │ └── HunyuanErrorHandler.java │ │ │ ├── AbstractErrorHandler.java │ │ │ └── ErrorHandler.java │ │ └── error │ │ │ ├── Error.java │ │ │ ├── OpenAiError.java │ │ │ └── HunyuanError.java │ │ ├── convert │ │ ├── audio │ │ │ ├── AudioResultConvert.java │ │ │ └── AudioParameterConvert.java │ │ └── chat │ │ │ ├── ParameterConvert.java │ │ │ └── ResultConvert.java │ │ ├── platform │ │ ├── openai │ │ │ ├── realtime │ │ │ │ ├── entity │ │ │ │ │ ├── ConversationCreated.java │ │ │ │ │ ├── SessionCreated.java │ │ │ │ │ ├── SessionUpdated.java │ │ │ │ │ └── Session.java │ │ │ │ ├── OpenAiRealtimeService.java │ │ │ │ └── RealtimeConstant.java │ │ │ ├── chat │ │ │ │ ├── enums │ │ │ │ │ └── ChatMessageType.java │ │ │ │ └── entity │ │ │ │ │ ├── StreamOptions.java │ │ │ │ │ ├── Choice.java │ │ │ │ │ ├── ChatCompletionResponse.java │ │ │ │ │ ├── ChatMessage.java │ │ │ │ │ └── ChatCompletion.java │ │ │ ├── audio │ │ │ │ ├── enums │ │ │ │ │ ├── WhisperEnum.java │ │ │ │ │ └── AudioEnum.java │ │ │ │ ├── entity │ │ │ │ │ ├── Word.java │ │ │ │ │ ├── TranslationResponse.java │ │ │ │ │ ├── TranscriptionResponse.java │ │ │ │ │ ├── Segment.java │ │ │ │ │ ├── TextToSpeech.java │ │ │ │ │ ├── Translation.java │ │ │ │ │ └── Transcription.java │ │ │ │ └── OpenAiAudioService.java │ │ │ ├── usage │ │ │ │ └── Usage.java │ │ │ ├── embedding │ │ │ │ ├── entity │ │ │ │ │ ├── EmbeddingResponse.java │ │ │ │ │ ├── EmbeddingObject.java │ │ │ │ │ └── Embedding.java │ │ │ │ └── OpenAiEmbeddingService.java │ │ │ └── tool │ │ │ │ ├── ToolCall.java │ │ │ │ └── Tool.java │ │ ├── hunyuan │ │ │ ├── HunyuanConstant.java │ │ │ └── chat │ │ │ │ └── entity │ │ │ │ ├── HunyuanChatCompletionResponse.java │ │ │ │ └── HunyuanChatCompletion.java │ │ ├── ollama │ │ │ └── chat │ │ │ │ └── entity │ │ │ │ ├── OllamaMessage.java │ │ │ │ ├── OllamaOptions.java │ │ │ │ ├── OllamaChatCompletion.java │ │ │ │ └── OllamaChatCompletionResponse.java │ │ ├── baichuan │ │ │ └── chat │ │ │ │ └── entity │ │ │ │ ├── BaichuanChatCompletionResponse.java │ │ │ │ └── BaichuanChatCompletion.java │ │ ├── zhipu │ │ │ └── chat │ │ │ │ └── entity │ │ │ │ ├── ZhipuChatCompletionResponse.java │ │ │ │ └── ZhipuChatCompletion.java │ │ ├── lingyi │ │ │ └── chat │ │ │ │ └── entity │ │ │ │ ├── LingyiChatCompletionResponse.java │ │ │ │ └── LingyiChatCompletion.java │ │ ├── minimax │ │ │ └── chat │ │ │ │ └── entity │ │ │ │ ├── MinimaxChatCompletionResponse.java │ │ │ │ └── MinimaxChatCompletion.java │ │ ├── deepseek │ │ │ └── chat │ │ │ │ └── entity │ │ │ │ ├── DeepSeekChatCompletionResponse.java │ │ │ │ └── DeepSeekChatCompletion.java │ │ └── moonshot │ │ │ └── chat │ │ │ └── entity │ │ │ ├── MoonshotChatCompletionResponse.java │ │ │ └── MoonshotChatCompletion.java │ │ ├── network │ │ ├── DispatcherProvider.java │ │ ├── ConnectionPoolProvider.java │ │ └── impl │ │ │ ├── DefaultDispatcherProvider.java │ │ │ └── DefaultConnectionPoolProvider.java │ │ ├── vector │ │ ├── pinecone │ │ │ ├── PineconeInsertResponse.java │ │ │ ├── PineconeDelete.java │ │ │ ├── PineconeInsert.java │ │ │ ├── PineconeVectors.java │ │ │ ├── PineconeQueryResponse.java │ │ │ └── PineconeQuery.java │ │ ├── VertorDataEntity.java │ │ └── service │ │ │ └── PineconeService.java │ │ ├── config │ │ ├── BaichuanConfig.java │ │ ├── LingyiConfig.java │ │ ├── MoonshotConfig.java │ │ ├── OllamaConfig.java │ │ ├── DeepSeekConfig.java │ │ ├── MinimaxConfig.java │ │ ├── HunyuanConfig.java │ │ ├── ZhipuConfig.java │ │ ├── PineconeConfig.java │ │ ├── OkHttpConfig.java │ │ └── OpenAiConfig.java │ │ ├── annotation │ │ ├── FunctionRequest.java │ │ ├── FunctionCall.java │ │ └── FunctionParameter.java │ │ ├── service │ │ ├── IRealtimeService.java │ │ ├── IEmbeddingService.java │ │ ├── IAudioService.java │ │ ├── PlatformType.java │ │ ├── IChatService.java │ │ ├── Configuration.java │ │ └── factor │ │ │ └── AiService.java │ │ ├── constant │ │ └── Constants.java │ │ ├── websearch │ │ ├── searxng │ │ │ ├── SearXNGConfig.java │ │ │ ├── SearXNGRequest.java │ │ │ └── SearXNGResponse.java │ │ └── ChatWithWebSearchEnhance.java │ │ ├── utils │ │ ├── ServiceLoaderUtil.java │ │ ├── ValidateUtil.java │ │ ├── OkHttpUtil.java │ │ ├── TikaUtil.java │ │ ├── TikTokensUtil.java │ │ ├── JsonObjectUtil.java │ │ ├── RecursiveCharacterTextSplitter.java │ │ └── BearerTokenUtils.java │ │ ├── tools │ │ ├── QueryTrainInfoFunction.java │ │ └── QueryWeatherFunction.java │ │ ├── listener │ │ └── RealtimeListener.java │ │ └── interceptor │ │ ├── ContentTypeInterceptor.java │ │ └── ErrorInterceptor.java │ └── test │ └── java │ └── io │ └── github │ └── lnyocly │ ├── OtherTest.java │ ├── BaichuanTest.java │ └── MinimaxTest.java ├── ai4j-spring-boot-starter └── src │ └── main │ ├── resources │ └── META-INF │ │ ├── spring │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── spring.factories │ └── java │ └── io │ └── github │ └── lnyocly │ └── ai4j │ ├── OllamaConfigProperties.java │ ├── LingyiConfigProperties.java │ ├── BaichuanConfigProperties.java │ ├── MoonshotConfigProperties.java │ ├── DeepSeekConfigProperties.java │ ├── MinimaxConfigProperties.java │ ├── HunyuanConfigProperties.java │ ├── ZhipuConfigProperties.java │ ├── SearXNGConfigProperties.java │ ├── PineconeConfigProperties.java │ ├── OpenAiConfigProperties.java │ └── OkHttpConfigProperties.java ├── .gitignore └── pom.xml /ai4j/src/main/resources/META-INF/services/io.github.lnyocly.ai4j.network.DispatcherProvider: -------------------------------------------------------------------------------- 1 | io.github.lnyocly.ai4j.network.impl.DefaultDispatcherProvider -------------------------------------------------------------------------------- /ai4j/src/main/resources/META-INF/services/io.github.lnyocly.ai4j.network.ConnectionPoolProvider: -------------------------------------------------------------------------------- 1 | io.github.lnyocly.ai4j.network.impl.DefaultConnectionPoolProvider -------------------------------------------------------------------------------- /ai4j-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.github.lnyocly.ai4j.AiConfigAutoConfiguration -------------------------------------------------------------------------------- /ai4j-spring-boot-starter/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | io.github.lnyocly.ai4j.AiConfigAutoConfiguration -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/exception/CommonException.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.exception; 2 | 3 | public class CommonException extends RuntimeException{ 4 | 5 | public CommonException(String msg){ 6 | super(msg); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/convert/audio/AudioResultConvert.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.convert.audio; 2 | 3 | /** 4 | * @Author cly 5 | * @Description 返回结果统一处理。 其它模型音频返回格式--->统一的OpenAi返回格式 6 | * @Date 2024/10/12 13:35 7 | */ 8 | public interface AudioResultConvert { 9 | } 10 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/realtime/entity/ConversationCreated.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.realtime.entity; 2 | 3 | /** 4 | * @Author cly 5 | * @Description TODO 6 | * @Date 2024/10/12 18:01 7 | */ 8 | public class ConversationCreated { 9 | } 10 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/convert/audio/AudioParameterConvert.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.convert.audio; 2 | 3 | /** 4 | * @Author cly 5 | * @Description 处理请求参数。 由统一的OpenAi音频格式--->其它模型格式 6 | * @Date 2024/10/12 13:36 7 | */ 8 | public interface AudioParameterConvert { 9 | } 10 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/network/DispatcherProvider.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.network; 2 | 3 | import okhttp3.Dispatcher; 4 | 5 | /** 6 | * @Author cly 7 | * @Description Dispatcher提供器 8 | * @Date 2024/10/16 23:09 9 | */ 10 | public interface DispatcherProvider { 11 | Dispatcher getDispatcher(); 12 | } 13 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/vector/pinecone/PineconeInsertResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.vector.pinecone; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @Author cly 7 | * @Description TODO 8 | * @Date 2024/8/16 17:28 9 | */ 10 | @Data 11 | public class PineconeInsertResponse { 12 | private Integer upsertedCount; 13 | } 14 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/network/ConnectionPoolProvider.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.network; 2 | 3 | import okhttp3.ConnectionPool; 4 | 5 | /** 6 | * @Author cly 7 | * @Description ConnectionPool提供器 8 | * @Date 2024/10/16 23:10 9 | */ 10 | public interface ConnectionPoolProvider { 11 | ConnectionPool getConnectionPool(); 12 | } 13 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/hunyuan/HunyuanConstant.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.hunyuan; 2 | 3 | /** 4 | * @Author cly 5 | * @Description 腾讯混元常量 6 | * @Date 2024/8/30 19:30 7 | */ 8 | public class HunyuanConstant { 9 | 10 | public static final String Version = "2023-09-01"; 11 | 12 | public static final String ChatCompletions = "ChatCompletions"; 13 | } 14 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/realtime/entity/SessionCreated.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.realtime.entity; 2 | 3 | /** 4 | * @Author cly 5 | * @Description TODO 6 | * @Date 2024/10/12 17:58 7 | */ 8 | public class SessionCreated { 9 | private String event_id; 10 | private String type = "session.created"; 11 | private Session session; 12 | } 13 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/realtime/entity/SessionUpdated.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.realtime.entity; 2 | 3 | /** 4 | * @Author cly 5 | * @Description TODO 6 | * @Date 2024/10/12 17:58 7 | */ 8 | public class SessionUpdated { 9 | private String event_id; 10 | private String type = "session.updated"; 11 | private Session session; 12 | } 13 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/convert/chat/ParameterConvert.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.convert.chat; 2 | 3 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatCompletion; 4 | 5 | /** 6 | * @Author cly 7 | * @Description 处理请求参数 统一的OpenAi格式--->其它模型格式 8 | * @Date 2024/8/12 1:04 9 | */ 10 | public interface ParameterConvert { 11 | T convertChatCompletionObject(ChatCompletion chatCompletion); 12 | } 13 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/exception/chain/IErrorHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.exception.chain; 2 | 3 | import io.github.lnyocly.ai4j.exception.error.Error; 4 | import io.github.lnyocly.ai4j.exception.error.OpenAiError; 5 | 6 | /** 7 | * @Author cly 8 | * @Description 错误处理接口 9 | * @Date 2024/9/18 20:55 10 | */ 11 | public interface IErrorHandler { 12 | void setNext(IErrorHandler handler); 13 | Error parseError(String errorInfo); 14 | } 15 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/config/BaichuanConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class BaichuanConfig { 11 | 12 | private String apiHost = "https://api.baichuan-ai.com/"; 13 | private String apiKey = ""; 14 | private String chatCompletionUrl = "v1/chat/completions"; 15 | } 16 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/network/impl/DefaultDispatcherProvider.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.network.impl; 2 | 3 | import io.github.lnyocly.ai4j.network.DispatcherProvider; 4 | import okhttp3.Dispatcher; 5 | 6 | /** 7 | * @Author cly 8 | * @Description Dispatcher默认实现 9 | * @Date 2024/10/16 23:11 10 | */ 11 | public class DefaultDispatcherProvider implements DispatcherProvider { 12 | @Override 13 | public Dispatcher getDispatcher() { 14 | return new Dispatcher(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/exception/error/Error.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.exception.error; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Author cly 9 | * @Description 基础错误实体 10 | * @Date 2024/9/18 23:50 11 | */ 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class Error { 16 | private String message; 17 | private String type; 18 | private String param; 19 | private String code; 20 | } 21 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/annotation/FunctionRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @Author cly 10 | * @Description TODO 11 | * @Date 2024/8/12 15:55 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.TYPE) 15 | public @interface FunctionRequest { 16 | String description() default ""; 17 | } -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/annotation/FunctionCall.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @Author cly 10 | * @Description TODO 11 | * @Date 2024/8/12 15:50 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.TYPE) 15 | public @interface FunctionCall { 16 | String name(); 17 | String description(); 18 | } 19 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/network/impl/DefaultConnectionPoolProvider.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.network.impl; 2 | 3 | import io.github.lnyocly.ai4j.network.ConnectionPoolProvider; 4 | import okhttp3.ConnectionPool; 5 | 6 | /** 7 | * @Author cly 8 | * @Description ConnectionPool默认实现 9 | * @Date 2024/10/16 23:11 10 | */ 11 | public class DefaultConnectionPoolProvider implements ConnectionPoolProvider { 12 | @Override 13 | public ConnectionPool getConnectionPool() { 14 | return new ConnectionPool(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/service/IRealtimeService.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.service; 2 | 3 | import io.github.lnyocly.ai4j.listener.RealtimeListener; 4 | import okhttp3.WebSocket; 5 | 6 | /** 7 | * @Author cly 8 | * @Description realtime服务接口 9 | * @Date 2024/10/12 16:30 10 | */ 11 | public interface IRealtimeService { 12 | WebSocket createRealtimeClient(String baseUrl, String apiKey, String model, RealtimeListener realtimeListener); 13 | WebSocket createRealtimeClient(String model, RealtimeListener realtimeListener); 14 | } 15 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/config/LingyiConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Author cly 9 | * @Description 零一万物大模型 10 | * @Date 2024/9/9 22:53 11 | */ 12 | @Data 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class LingyiConfig { 16 | private String apiHost = "https://api.lingyiwanwu.com/"; 17 | private String apiKey = ""; 18 | private String chatCompletionUrl = "v1/chat/completions"; 19 | } 20 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/vector/pinecone/PineconeDelete.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.vector.pinecone; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | /** 10 | * @Author cly 11 | * @Description TODO 12 | * @Date 2024/8/15 0:00 13 | */ 14 | 15 | @Builder 16 | @Data 17 | public class PineconeDelete { 18 | private List ids; 19 | 20 | private boolean deleteAll; 21 | 22 | private String namespace; 23 | 24 | private Map filter; 25 | } 26 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/config/MoonshotConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Author cly 9 | * @Description 月之暗面配置 10 | * @Date 2024/8/29 23:00 11 | */ 12 | 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class MoonshotConfig { 17 | private String apiHost = "https://api.moonshot.cn/"; 18 | private String apiKey = ""; 19 | private String chatCompletionUrl = "v1/chat/completions"; 20 | } 21 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/annotation/FunctionParameter.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @Author cly 10 | * @Description TODO 11 | * @Date 2024/8/12 15:55 12 | */ 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.FIELD) 15 | public @interface FunctionParameter { 16 | String description(); 17 | boolean required() default true; 18 | } -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/config/OllamaConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Author cly 9 | * @Description Ollama配置文件 10 | * @Date 2024/9/20 11:07 11 | */ 12 | 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class OllamaConfig { 17 | private String apiHost = "http://localhost:11434/"; 18 | private String chatCompletionUrl = "api/chat"; 19 | private String embeddingUrl = "api/embed"; 20 | } 21 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/config/DeepSeekConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Author cly 9 | * @Description DeepSeek 配置文件 10 | * @Date 2024/8/29 10:31 11 | */ 12 | 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class DeepSeekConfig { 17 | 18 | private String apiHost = "https://api.deepseek.com/"; 19 | private String apiKey = ""; 20 | private String chatCompletionUrl = "chat/completions"; 21 | } 22 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/chat/enums/ChatMessageType.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.chat.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Author cly 9 | * @Description TODO 10 | * @Date 2024/8/6 23:54 11 | */ 12 | @Getter 13 | @AllArgsConstructor 14 | public enum ChatMessageType { 15 | SYSTEM("system"), 16 | USER("user"), 17 | ASSISTANT("assistant"), 18 | TOOL("tool"), 19 | ; 20 | 21 | private final String role; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /ai4j-spring-boot-starter/src/main/java/io/github/lnyocly/ai4j/OllamaConfigProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * @Author cly 8 | * @Description Ollama配置文件 9 | * @Date 2024/9/20 23:01 10 | */ 11 | @Data 12 | @ConfigurationProperties(prefix = "ai.ollama") 13 | public class OllamaConfigProperties { 14 | private String apiHost = "http://localhost:11434/"; 15 | private String chatCompletionUrl = "api/chat"; 16 | private String embeddingUrl = "api/embed"; 17 | } 18 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/config/MinimaxConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Author : isxuwl 9 | * @Date: 2024/10/15 16:08 10 | * @Model Description: 11 | * @Description: 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class MinimaxConfig { 17 | private String apiHost = "https://api.minimax.chat/"; 18 | private String apiKey = ""; 19 | private String chatCompletionUrl = "v1/text/chatcompletion_v2"; 20 | } 21 | -------------------------------------------------------------------------------- /ai4j-spring-boot-starter/src/main/java/io/github/lnyocly/ai4j/LingyiConfigProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * @Author cly 8 | * @Description 零一万物配置文件 9 | * @Date 2024/9/9 23:31 10 | */ 11 | 12 | @Data 13 | @ConfigurationProperties(prefix = "ai.lingyi") 14 | public class LingyiConfigProperties { 15 | private String apiHost = "https://api.lingyiwanwu.com/"; 16 | private String apiKey = ""; 17 | private String chatCompletionUrl = "v1/chat/completions"; 18 | } 19 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/config/HunyuanConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Author cly 9 | * @Description 腾讯混元配置 10 | * @Date 2024/8/30 19:50 11 | */ 12 | @Data 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class HunyuanConfig { 16 | private String apiHost = "https://hunyuan.tencentcloudapi.com/"; 17 | /** 18 | * apiKey 属于SecretId与SecretKey的拼接,格式为 {SecretId}.{SecretKey} 19 | */ 20 | private String apiKey = ""; 21 | } 22 | -------------------------------------------------------------------------------- /ai4j-spring-boot-starter/src/main/java/io/github/lnyocly/ai4j/BaichuanConfigProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * @Author cly 8 | * @Description 智谱 配置文件 9 | * @Date 2024/8/28 17:39 10 | */ 11 | 12 | @Data 13 | @ConfigurationProperties(prefix = "ai.baichuan") 14 | public class BaichuanConfigProperties { 15 | private String apiHost = "https://api.baichuan-ai.com/"; 16 | private String apiKey = ""; 17 | private String chatCompletionUrl = "v1/chat/completions"; 18 | } 19 | -------------------------------------------------------------------------------- /ai4j-spring-boot-starter/src/main/java/io/github/lnyocly/ai4j/MoonshotConfigProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * @Author cly 8 | * @Description Moonshot配置文件 9 | * @Date 2024/8/30 15:56 10 | */ 11 | 12 | @Data 13 | @ConfigurationProperties(prefix = "ai.moonshot") 14 | public class MoonshotConfigProperties { 15 | private String apiHost = "https://api.moonshot.cn/"; 16 | private String apiKey = ""; 17 | private String chatCompletionUrl = "v1/chat/completions"; 18 | } 19 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/service/IEmbeddingService.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.service; 2 | 3 | import io.github.lnyocly.ai4j.platform.openai.embedding.entity.Embedding; 4 | import io.github.lnyocly.ai4j.platform.openai.embedding.entity.EmbeddingResponse; 5 | 6 | /** 7 | * @Author cly 8 | * @Description TODO 9 | * @Date 2024/8/2 23:15 10 | */ 11 | public interface IEmbeddingService { 12 | 13 | EmbeddingResponse embedding(String baseUrl, String apiKey, Embedding embeddingReq) throws Exception ; 14 | EmbeddingResponse embedding(Embedding embeddingReq) throws Exception ; 15 | } 16 | -------------------------------------------------------------------------------- /ai4j-spring-boot-starter/src/main/java/io/github/lnyocly/ai4j/DeepSeekConfigProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * @Author cly 8 | * @Description DeepSeek配置文件 9 | * @Date 2024/8/29 15:01 10 | */ 11 | 12 | @Data 13 | @ConfigurationProperties(prefix = "ai.deepseek") 14 | public class DeepSeekConfigProperties { 15 | 16 | private String apiHost = "https://api.deepseek.com/"; 17 | private String apiKey = ""; 18 | private String chatCompletionUrl = "chat/completions"; 19 | } 20 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/convert/chat/ResultConvert.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.convert.chat; 2 | 3 | import io.github.lnyocly.ai4j.listener.SseListener; 4 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatCompletionResponse; 5 | import okhttp3.sse.EventSourceListener; 6 | 7 | /** 8 | * @Author cly 9 | * @Description 处理结果输出 其它模型格式--->统一的OpenAi格式 10 | * @Date 2024/8/12 1:05 11 | */ 12 | public interface ResultConvert { 13 | EventSourceListener convertEventSource(SseListener eventSourceListener); 14 | ChatCompletionResponse convertChatCompletionResponse(T t); 15 | } 16 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/config/ZhipuConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Author cly 9 | * @Description 智谱AI平台配置信息 10 | * @Date 2024/8/27 22:12 11 | */ 12 | @Data 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class ZhipuConfig { 16 | 17 | private String apiHost = "https://open.bigmodel.cn/api/paas/"; 18 | private String apiKey = ""; 19 | private String chatCompletionUrl = "v4/chat/completions"; 20 | private String embeddingUrl= "v4/embeddings"; 21 | } 22 | -------------------------------------------------------------------------------- /ai4j-spring-boot-starter/src/main/java/io/github/lnyocly/ai4j/MinimaxConfigProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * @Author : isxuwl 8 | * @Date: 2024/10/15 16:27 9 | * @Model Description: 10 | * @Description: 11 | */ 12 | 13 | @Data 14 | @ConfigurationProperties(prefix = "ai.minimax") 15 | public class MinimaxConfigProperties { 16 | private String apiHost = "https://api.minimax.chat/"; 17 | private String apiKey = ""; 18 | private String chatCompletionUrl = "v1/text/chatcompletion_v2"; 19 | } 20 | -------------------------------------------------------------------------------- /ai4j-spring-boot-starter/src/main/java/io/github/lnyocly/ai4j/HunyuanConfigProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * @Author cly 8 | * @Description 腾讯混元配置文件 9 | * @Date 2024/9/2 19:13 10 | */ 11 | 12 | @Data 13 | @ConfigurationProperties(prefix = "ai.hunyuan") 14 | public class HunyuanConfigProperties { 15 | private String apiHost = "https://hunyuan.tencentcloudapi.com/"; 16 | /** 17 | * apiKey 属于SecretId与SecretKey的拼接,格式为 {SecretId}.{SecretKey} 18 | */ 19 | private String apiKey = ""; 20 | } 21 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/constant/Constants.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.constant; 2 | 3 | /** 4 | * @Author cly 5 | * @Description TODO 6 | * @Date 2024/8/11 0:19 7 | */ 8 | public class Constants { 9 | public static final String SSE_CONTENT_TYPE = "text/event-stream"; 10 | public static final String DEFAULT_USER_AGENT = "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)"; 11 | public static final String APPLICATION_JSON = "application/json"; 12 | public static final String JSON_CONTENT_TYPE = APPLICATION_JSON + "; charset=utf-8"; 13 | public static final String METADATA_KEY = "content"; 14 | } 15 | -------------------------------------------------------------------------------- /ai4j-spring-boot-starter/src/main/java/io/github/lnyocly/ai4j/ZhipuConfigProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * @Author cly 8 | * @Description 智谱 配置文件 9 | * @Date 2024/8/28 17:39 10 | */ 11 | 12 | @Data 13 | @ConfigurationProperties(prefix = "ai.zhipu") 14 | public class ZhipuConfigProperties { 15 | private String apiHost = "https://open.bigmodel.cn/api/paas/"; 16 | private String apiKey = ""; 17 | private String chatCompletionUrl = "v4/chat/completions"; 18 | private String embeddingUrl = "v4/embeddings"; 19 | } 20 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/config/PineconeConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Author cly 9 | * @Description Pinecone向量数据配置文件 10 | * @Date 2024/8/16 16:37 11 | */ 12 | 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class PineconeConfig { 17 | private String host = "https://xxx.svc.xxx.pinecone.io"; 18 | private String key = ""; 19 | 20 | private String upsert = "/vectors/upsert"; 21 | private String query = "/query"; 22 | private String delete = "/vectors/delete"; 23 | } 24 | -------------------------------------------------------------------------------- /ai4j-spring-boot-starter/src/main/java/io/github/lnyocly/ai4j/SearXNGConfigProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * @Author cly 8 | * @Description TODO 9 | * @Date 2024/12/12 11:55 10 | */ 11 | @Data 12 | @ConfigurationProperties(prefix = "ai.websearch.searxng") 13 | public class SearXNGConfigProperties { 14 | private String url; 15 | private String engines = "duckduckgo,google,bing,brave,mojeek,presearch,qwant,startpage,yahoo,arxiv,crossref,google_scholar,internetarchivescholar,semantic_scholar"; 16 | private int nums = 20; 17 | } 18 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/vector/pinecone/PineconeInsert.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.vector.pinecone; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @Author cly 12 | * @Description TODO 13 | * @Date 2024/8/14 20:08 14 | */ 15 | 16 | @Data 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | @Builder 20 | public class PineconeInsert { 21 | /** 22 | * 需要插入的文本的向量库 23 | */ 24 | private List vectors; 25 | 26 | /** 27 | * 命名空间,用于区分每个文本 28 | */ 29 | private String namespace; 30 | } 31 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/websearch/searxng/SearXNGConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.websearch.searxng; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import lombok.NonNull; 7 | 8 | /** 9 | * @Author cly 10 | * @Description SearXNG网路搜索配置信息 11 | * @Date 2024/12/11 23:05 12 | */ 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class SearXNGConfig { 17 | private String url; 18 | private String engines = "duckduckgo,google,bing,brave,mojeek,presearch,qwant,startpage,yahoo,arxiv,crossref,google_scholar,internetarchivescholar,semantic_scholar"; 19 | private int nums = 20; 20 | } 21 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/audio/enums/WhisperEnum.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.audio.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * @Author cly 10 | * @Description Whisper枚举类 11 | * @Date 2024/10/10 23:56 12 | */ 13 | public class WhisperEnum { 14 | @Getter 15 | @AllArgsConstructor 16 | public enum ResponseFormat implements Serializable { 17 | JSON("json"), 18 | TEXT("text"), 19 | SRT("srt"), 20 | VERBOSE_JSON("verbose_json"), 21 | VTT("vtt"), 22 | ; 23 | private final String value; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ai4j-spring-boot-starter/src/main/java/io/github/lnyocly/ai4j/PineconeConfigProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * @Author cly 8 | * @Description Pinecone 向量数据配置文件 9 | * @Date 2024/8/16 16:37 10 | */ 11 | 12 | @Data 13 | @ConfigurationProperties(prefix = "ai.vector.pinecone") 14 | public class PineconeConfigProperties { 15 | private String host = "https://xxx.svc.xxx.pinecone.io"; 16 | private String key = ""; 17 | 18 | private String upsert = "/vectors/upsert"; 19 | private String query = "/query"; 20 | private String delete = "/vectors/delete"; 21 | } 22 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/vector/VertorDataEntity.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.vector; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @Author cly 11 | * @Description TODO 12 | * @Date 2024/8/14 18:23 13 | */ 14 | @Data 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class VertorDataEntity { 18 | 19 | /** 20 | * 分段后的每一段的向量 21 | */ 22 | private List> vector; 23 | 24 | /** 25 | * 每一段的内容 26 | */ 27 | private List content; 28 | 29 | /** 30 | * 总共token数量 31 | */ 32 | //private Integer total_token; 33 | } 34 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/audio/entity/Word.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.audio.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * @Author cly 11 | * @Description word类 12 | * @Date 2024/10/11 12:04 13 | */ 14 | 15 | @Data 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | @JsonInclude(JsonInclude.Include.NON_NULL) 19 | @JsonIgnoreProperties(ignoreUnknown = true) 20 | public class Word { 21 | private String word; 22 | private Double start; 23 | private Double end; 24 | } 25 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/utils/ServiceLoaderUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.utils; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.util.ServiceLoader; 6 | 7 | /** 8 | * @Author cly 9 | * @Description SPI服务加载类 10 | * @Date 2024/10/16 23:25 11 | */ 12 | @Slf4j 13 | public class ServiceLoaderUtil { 14 | public static T load(Class service) { 15 | ServiceLoader loader = ServiceLoader.load(service); 16 | for (T impl : loader) { 17 | log.info("Loaded SPI implementation: {}", impl.getClass().getSimpleName()); 18 | return impl; 19 | } 20 | throw new IllegalStateException("No implementation found for " + service.getName()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/config/OkHttpConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import okhttp3.logging.HttpLoggingInterceptor; 7 | 8 | /** 9 | * @Author cly 10 | * @Description OkHttp配置信息 11 | * @Date 2024/8/11 0:11 12 | */ 13 | @Data 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class OkHttpConfig { 17 | 18 | private HttpLoggingInterceptor.Level log = HttpLoggingInterceptor.Level.HEADERS; 19 | private int connectTimeout = 300; 20 | private int writeTimeout = 300; 21 | private int readTimeout = 300; 22 | private int proxyPort = 10809; 23 | private String proxyHost = ""; 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/usage/Usage.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.usage; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import lombok.Data; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * @Author cly 11 | * @Description TODO 12 | * @Date 2024/8/7 17:38 13 | */ 14 | @Data 15 | @JsonIgnoreProperties(ignoreUnknown = true) 16 | public class Usage implements Serializable { 17 | @JsonProperty("prompt_tokens") 18 | private long promptTokens = 0L; 19 | @JsonProperty("completion_tokens") 20 | private long completionTokens = 0L; 21 | @JsonProperty("total_tokens") 22 | private long totalTokens = 0L; 23 | } -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/exception/chain/impl/UnknownErrorHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.exception.chain.impl; 2 | 3 | import io.github.lnyocly.ai4j.exception.chain.AbstractErrorHandler; 4 | import io.github.lnyocly.ai4j.exception.error.Error; 5 | 6 | /** 7 | * @Author cly 8 | * @Description 未知的错误处理,用于兜底处理 9 | * @Date 2024/9/18 21:08 10 | */ 11 | public class UnknownErrorHandler extends AbstractErrorHandler { 12 | @Override 13 | public Error parseError(String errorInfo) { 14 | Error error = new Error(); 15 | 16 | error.setParam(null); 17 | error.setType("Unknown Type"); 18 | error.setCode("Unknown Code"); 19 | error.setMessage(errorInfo); 20 | 21 | return error; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/websearch/searxng/SearXNGRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.websearch.searxng; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @Author cly 10 | * @Description TODO 11 | * @Date 2024/12/11 21:41 12 | */ 13 | @Data 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | @Builder 17 | public class SearXNGRequest { 18 | @Builder.Default 19 | private final String format = "json"; 20 | 21 | private String q; 22 | 23 | @Builder.Default 24 | private String engines = "duckduckgo,google,bing,brave,mojeek,presearch,qwant,startpage,yahoo,arxiv,crossref,google_scholar,internetarchivescholar,semantic_scholar"; 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store 39 | /.idea/ 40 | /ai4j/.gitignore 41 | /ai4j/src/main/resources/新建文本文档 (2).txt 42 | /ai4j-spring-boot-stater/.gitignore 43 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/ollama/chat/entity/OllamaMessage.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.ollama.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.github.lnyocly.ai4j.platform.openai.tool.ToolCall; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @Author cly 13 | * @Description TODO 14 | * @Date 2024/9/20 0:25 15 | */ 16 | 17 | @Data 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | public class OllamaMessage { 21 | private String role; 22 | private String content; 23 | private List images; 24 | 25 | @JsonProperty("tool_calls") 26 | private List toolCalls; 27 | } 28 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/exception/chain/AbstractErrorHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.exception.chain; 2 | 3 | import io.github.lnyocly.ai4j.exception.error.Error; 4 | import io.github.lnyocly.ai4j.exception.error.OpenAiError; 5 | 6 | /** 7 | * @Author cly 8 | * @Description 错误处理抽象 9 | * @Date 2024/9/18 20:57 10 | */ 11 | public abstract class AbstractErrorHandler implements IErrorHandler{ 12 | protected IErrorHandler nextHandler; 13 | 14 | @Override 15 | public void setNext(IErrorHandler handler) { 16 | this.nextHandler = handler; 17 | } 18 | 19 | protected Error handleNext(String errorInfo) { 20 | if (nextHandler != null) { 21 | return nextHandler.parseError(errorInfo); 22 | } 23 | return null; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/chat/entity/StreamOptions.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | /** 12 | * @Author cly 13 | * @Description 流式输出相关选项 14 | * @Date 2024/8/29 13:00 15 | */ 16 | @Data 17 | @NoArgsConstructor() 18 | @AllArgsConstructor() 19 | @JsonIgnoreProperties(ignoreUnknown = true) 20 | @JsonInclude(JsonInclude.Include.NON_NULL) 21 | public class StreamOptions { 22 | @JsonProperty("include_usage") 23 | private Boolean includeUsage = true; 24 | } 25 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/config/OpenAiConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.config; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Author cly 9 | * @Description OpenAi平台配置文件信息 10 | * @Date 2024/8/8 0:18 11 | */ 12 | @Data 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class OpenAiConfig { 16 | private String apiHost = "https://api.openai.com/"; 17 | private String apiKey = ""; 18 | private String chatCompletionUrl = "v1/chat/completions"; 19 | private String embeddingUrl = "v1/embeddings"; 20 | private String speechUrl = "v1/audio/speech"; 21 | private String transcriptionUrl = "v1/audio/transcriptions"; 22 | private String translationUrl = "v1/audio/translations"; 23 | private String realtimeUrl = "v1/realtime"; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/service/IAudioService.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.service; 2 | 3 | import io.github.lnyocly.ai4j.platform.openai.audio.entity.*; 4 | 5 | import java.io.InputStream; 6 | 7 | /** 8 | * @Author cly 9 | * @Description 音频audio接口服务 10 | * @Date 2024/10/10 23:39 11 | */ 12 | public interface IAudioService { 13 | 14 | InputStream textToSpeech(String baseUrl, String apiKey, TextToSpeech textToSpeech); 15 | InputStream textToSpeech(TextToSpeech textToSpeech); 16 | 17 | TranscriptionResponse transcription(String baseUrl, String apiKey, Transcription transcription); 18 | TranscriptionResponse transcription(Transcription transcription); 19 | 20 | TranslationResponse translation(String baseUrl, String apiKey, Translation translation); 21 | TranslationResponse translation(Translation translation); 22 | } 23 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/audio/entity/TranslationResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.audio.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @Author cly 13 | * @Description TODO 14 | * @Date 2024/10/11 16:30 15 | */ 16 | @Data 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | @JsonInclude(JsonInclude.Include.NON_NULL) 20 | @JsonIgnoreProperties(ignoreUnknown = true) 21 | public class TranslationResponse { 22 | private String task; 23 | private String language; 24 | private Double duration; 25 | private String text; 26 | private List segments; 27 | } 28 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/embedding/entity/EmbeddingResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.embedding.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import io.github.lnyocly.ai4j.platform.openai.usage.Usage; 6 | import lombok.*; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @Author cly 12 | * @Description Embedding接口的返回结果 13 | * @Date 2024/8/7 17:44 14 | */ 15 | @Data 16 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 17 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 18 | @JsonIgnoreProperties(ignoreUnknown = true) 19 | @JsonInclude(JsonInclude.Include.NON_NULL) 20 | public class EmbeddingResponse { 21 | private String object; 22 | private List data; 23 | private String model; 24 | private Usage usage; 25 | } 26 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/audio/entity/TranscriptionResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.audio.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @Author cly 13 | * @Description TODO 14 | * @Date 2024/10/11 16:28 15 | */ 16 | @Data 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | @JsonInclude(JsonInclude.Include.NON_NULL) 20 | @JsonIgnoreProperties(ignoreUnknown = true) 21 | public class TranscriptionResponse { 22 | private String task; 23 | private String language; 24 | private Double duration; 25 | private String text; 26 | private List segments; 27 | private List words; 28 | } 29 | -------------------------------------------------------------------------------- /ai4j/src/test/java/io/github/lnyocly/OtherTest.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly; 2 | 3 | import io.github.lnyocly.ai4j.platform.openai.chat.enums.ChatMessageType; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.junit.Test; 6 | 7 | import java.time.Instant; 8 | import java.util.List; 9 | import java.util.UUID; 10 | 11 | /** 12 | * @Author cly 13 | * @Description TODO 14 | * @Date 2024/9/20 11:41 15 | */ 16 | @Slf4j 17 | public class OtherTest { 18 | 19 | @Test 20 | public void test(){ 21 | long currentTimeMillis = System.currentTimeMillis(); 22 | 23 | String isoDateTime = "2024-07-22T20:33:28.123648Z"; 24 | Instant instant = Instant.parse(isoDateTime); 25 | long epochSeconds = instant.getEpochSecond(); 26 | System.out.println("Epoch seconds: " + epochSeconds); 27 | 28 | 29 | System.out.println(System.currentTimeMillis() - currentTimeMillis); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/exception/error/OpenAiError.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.exception.error; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.Getter; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @Author cly 10 | * @Description OpenAi错误返回实体类 11 | * 12 | * { 13 | * "error": { 14 | * "message": "Incorrect API key provided: sk-proj-*************************************************************************************************************************8YA1. You can find your API key at https://platform.openai.com/account/api-keys.", 15 | * "type": "invalid_request_error", 16 | * "param": null, 17 | * "code": "invalid_api_key" 18 | * } 19 | * } 20 | * 21 | * @Date 2024/9/18 18:44 22 | */ 23 | @Data 24 | @AllArgsConstructor 25 | @NoArgsConstructor 26 | public class OpenAiError { 27 | private Error error; 28 | } 29 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/vector/pinecone/PineconeVectors.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.vector.pinecone; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * @Author cly 13 | * @Description TODO 14 | * @Date 2024/8/14 20:07 15 | */ 16 | @Data 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | @Builder 20 | public class PineconeVectors { 21 | /** 22 | * 每条向量的id 23 | */ 24 | private String id; 25 | 26 | /** 27 | * 分段后每一段的向量 28 | */ 29 | private List values; 30 | 31 | /** 32 | * 向量稀疏数据。表示为索引列表和对应值列表,它们必须具有相同的长度。 33 | */ 34 | //private Map sparseValues; 35 | 36 | /** 37 | * 元数据,可以用来存储向量对应的文本 { key: "content", value: "对应文本" } 38 | */ 39 | private Map metadata; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/baichuan/chat/entity/BaichuanChatCompletionResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.baichuan.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.Choice; 6 | import io.github.lnyocly.ai4j.platform.openai.usage.Usage; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | import java.util.List; 12 | 13 | 14 | @Data 15 | @NoArgsConstructor() 16 | @AllArgsConstructor() 17 | @JsonIgnoreProperties(ignoreUnknown = true) 18 | @JsonInclude(JsonInclude.Include.NON_NULL) 19 | public class BaichuanChatCompletionResponse { 20 | private String id; 21 | private String object; 22 | private Long created; 23 | private String model; 24 | private List choices; 25 | private Usage usage; 26 | } 27 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/exception/error/HunyuanError.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.exception.error; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Author cly 9 | * @Description 腾讯混元错误实体 10 | * 11 | * {"Response":{"RequestId":"e4650694-f018-4490-b4d0-d5242cd68106","Error":{"Code":"InvalidParameterValue.Model","Message":"模型不存在"}}} 12 | * 13 | * @Date 2024/9/18 21:28 14 | */ 15 | @Data 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | public class HunyuanError { 19 | private Response Response; 20 | 21 | @Data 22 | @AllArgsConstructor 23 | @NoArgsConstructor 24 | public class Response{ 25 | private Error Error; 26 | 27 | @Data 28 | @AllArgsConstructor 29 | @NoArgsConstructor 30 | public class Error{ 31 | private String Code; 32 | private String Message; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/embedding/entity/EmbeddingObject.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.embedding.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.*; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @Author cly 11 | * @Description embedding的处理结果 12 | * @Date 2024/8/7 17:30 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 17 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 18 | @JsonIgnoreProperties(ignoreUnknown = true) 19 | @JsonInclude(JsonInclude.Include.NON_NULL) 20 | public class EmbeddingObject { 21 | /** 22 | * 结果下标 23 | */ 24 | private Integer index; 25 | 26 | /** 27 | * embedding的处理结果,返回向量化表征的数组 28 | */ 29 | private List embedding; 30 | 31 | /** 32 | * 结果类型,目前为恒为 embedding 33 | */ 34 | private String object; 35 | } 36 | -------------------------------------------------------------------------------- /ai4j-spring-boot-starter/src/main/java/io/github/lnyocly/ai4j/OpenAiConfigProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | 7 | /** 8 | * @Author cly 9 | * @Description OpenAI配置文件 10 | * @Date 2024/8/9 23:17 11 | */ 12 | 13 | @Data 14 | @NoArgsConstructor 15 | @ConfigurationProperties(prefix = "ai.openai") 16 | public class OpenAiConfigProperties { 17 | private String apiHost = "https://api.openai.com/"; 18 | //private String wsHost = "wss://api.openai.com/"; 19 | private String apiKey = ""; 20 | private String chatCompletionUrl = "v1/chat/completions"; 21 | private String embeddingUrl = "v1/embeddings"; 22 | private String speechUrl = "v1/audio/speech"; 23 | private String transcriptionUrl = "v1/audio/transcriptions"; 24 | private String translationUrl = "v1/audio/translations"; 25 | private String realtimeUrl = "v1/realtime"; 26 | } 27 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/service/PlatformType.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.service; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * @Author cly 8 | * @Description TODO 9 | * @Date 2024/8/8 17:29 10 | */ 11 | @AllArgsConstructor 12 | @Getter 13 | public enum PlatformType { 14 | OPENAI("openai"), 15 | ZHIPU("zhipu"), 16 | DEEPSEEK("deepseek"), 17 | MOONSHOT("moonshot"), 18 | HUNYUAN("hunyuan"), 19 | LINGYI("lingyi"), 20 | OLLAMA("ollama"), 21 | MINIMAX("minimax"), 22 | BAICHUAN("baichuan"), 23 | ; 24 | private final String platform; 25 | 26 | public static PlatformType getPlatform(String value) { 27 | String target = value.toLowerCase(); 28 | for (PlatformType platformType : PlatformType.values()) { 29 | if (platformType.getPlatform().equals(target)) { 30 | return platformType; 31 | } 32 | } 33 | return PlatformType.OPENAI; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/tool/ToolCall.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.tool; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | /** 10 | * @Author cly 11 | * @Description TODO 12 | * @Date 2024/8/13 2:07 13 | */ 14 | @Data 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | @JsonIgnoreProperties(ignoreUnknown = true) 18 | @JsonInclude(JsonInclude.Include.NON_NULL) 19 | public class ToolCall { 20 | private String id; 21 | private String type; 22 | private Function function; 23 | 24 | 25 | @Data 26 | @AllArgsConstructor 27 | @NoArgsConstructor 28 | @JsonIgnoreProperties(ignoreUnknown = true) 29 | @JsonInclude(JsonInclude.Include.NON_NULL) 30 | public static class Function { 31 | private String name; 32 | private String arguments; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/audio/enums/AudioEnum.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.audio.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * @Author cly 10 | * @Description 音频audio枚举类 11 | * @Date 2024/10/10 23:49 12 | */ 13 | public class AudioEnum { 14 | @Getter 15 | @AllArgsConstructor 16 | public enum Voice implements Serializable { 17 | ALLOY("alloy"), 18 | ECHO("echo"), 19 | FABLE("fable"), 20 | ONYX("onyx"), 21 | NOVA("nova"), 22 | SHIMMER("shimmer"), 23 | ; 24 | private final String value; 25 | } 26 | 27 | @Getter 28 | @AllArgsConstructor 29 | public enum ResponseFormat implements Serializable { 30 | MP3("mp3"), 31 | OPUS("opus"), 32 | AAC("aac"), 33 | FLAC("flac"), 34 | WAV("wav"), 35 | PCM("pcm"), 36 | ; 37 | private final String value; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/vector/pinecone/PineconeQueryResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.vector.pinecone; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * @Author cly 10 | * @Description TODO 11 | * @Date 2024/8/15 0:05 12 | */ 13 | 14 | @Data 15 | public class PineconeQueryResponse { 16 | private List results; 17 | 18 | /** 19 | * 匹配的结果 20 | */ 21 | private List matches; 22 | 23 | /** 24 | * 命名空间 25 | */ 26 | private String namespace; 27 | 28 | @Data 29 | public static class Match { 30 | /** 31 | * 向量id 32 | */ 33 | private String id; 34 | 35 | /** 36 | * 相似度分数 37 | */ 38 | private Float score; 39 | 40 | /** 41 | * 向量 42 | */ 43 | private List values; 44 | 45 | /** 46 | * 向量的元数据,存放对应文本 47 | */ 48 | private Map metadata; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/ollama/chat/entity/OllamaOptions.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.ollama.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Builder; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @Author cly 13 | * @Description TODO 14 | * @Date 2024/9/20 0:30 15 | */ 16 | @Data 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class OllamaOptions { 20 | 21 | /** 22 | * 采样温度,介于 0 和 2 之间。更高的值,如 0.8,会使输出更随机,而更低的值,如 0.2,会使其更加集中和确定。 23 | * 我们通常建议可以更改这个值或者更改 top_p,但不建议同时对两者进行修改。 24 | */ 25 | private Float temperature = 0.8f; 26 | 27 | /** 28 | * 作为调节采样温度的替代方案,模型会考虑前 top_p 概率的 token 的结果。所以 0.1 就意味着只有包括在最高 10% 概率中的 token 会被考虑。 29 | * 我们通常建议修改这个值或者更改 temperature,但不建议同时对两者进行修改。 30 | */ 31 | @JsonProperty("top_p") 32 | private Float topP = 0.9f; 33 | 34 | 35 | private List stop; 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/zhipu/chat/entity/ZhipuChatCompletionResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.zhipu.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.Choice; 6 | import io.github.lnyocly.ai4j.platform.openai.usage.Usage; 7 | import lombok.AccessLevel; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @Author cly 16 | * @Description 智谱聊天请求响应实体类 17 | * @Date 2024/8/27 20:34 18 | */ 19 | 20 | @Data 21 | @NoArgsConstructor() 22 | @AllArgsConstructor() 23 | @JsonIgnoreProperties(ignoreUnknown = true) 24 | @JsonInclude(JsonInclude.Include.NON_NULL) 25 | public class ZhipuChatCompletionResponse { 26 | private String id; 27 | private String object; 28 | private Long created; 29 | private String model; 30 | private List choices; 31 | private Usage usage; 32 | } 33 | -------------------------------------------------------------------------------- /ai4j-spring-boot-starter/src/main/java/io/github/lnyocly/ai4j/OkHttpConfigProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j; 2 | 3 | import lombok.Data; 4 | import okhttp3.logging.HttpLoggingInterceptor; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | 7 | import java.net.Proxy; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * @Author cly 12 | * @Description OkHttp配置文件 13 | * @Date 2024/8/10 0:49 14 | */ 15 | 16 | @Data 17 | @ConfigurationProperties(prefix = "ai.okhttp") 18 | public class OkHttpConfigProperties { 19 | 20 | private Proxy.Type proxyType = Proxy.Type.HTTP; 21 | private String proxyUrl = ""; 22 | private int proxyPort; 23 | 24 | private HttpLoggingInterceptor.Level log = HttpLoggingInterceptor.Level.HEADERS; 25 | private int connectTimeout = 300; 26 | private int writeTimeout = 300; 27 | private int readTimeout = 300; 28 | private TimeUnit timeUnit = TimeUnit.SECONDS; 29 | 30 | /** 31 | * 忽略SSL证书,用于请求Moonshot(Kimi),其它平台可以不用忽略 32 | */ 33 | private boolean ignoreSsl = true; 34 | } 35 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/service/IChatService.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.service; 2 | 3 | import io.github.lnyocly.ai4j.listener.SseListener; 4 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatCompletion; 5 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatCompletionResponse; 6 | import io.github.lnyocly.ai4j.platform.openai.embedding.entity.Embedding; 7 | import okhttp3.sse.EventSource; 8 | import okhttp3.sse.EventSourceListener; 9 | 10 | /** 11 | * @Author cly 12 | * @Description TODO 13 | * @Date 2024/8/2 23:15 14 | */ 15 | public interface IChatService { 16 | 17 | ChatCompletionResponse chatCompletion(String baseUrl, String apiKey, ChatCompletion chatCompletion) throws Exception; 18 | ChatCompletionResponse chatCompletion(ChatCompletion chatCompletion) throws Exception; 19 | void chatCompletionStream(String baseUrl, String apiKey, ChatCompletion chatCompletion, SseListener eventSourceListener) throws Exception; 20 | void chatCompletionStream(ChatCompletion chatCompletion, SseListener eventSourceListener) throws Exception; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/websearch/searxng/SearXNGResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.websearch.searxng; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import java.util.Date; 9 | import java.util.List; 10 | 11 | /** 12 | * @Author cly 13 | * @Description TODO 14 | * @Date 2024/12/11 21:39 15 | */ 16 | @Data 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class SearXNGResponse { 20 | private String query; 21 | 22 | @JsonProperty("number_of_results") 23 | private String numberOfResults; 24 | 25 | private List results; 26 | 27 | 28 | @Data 29 | @AllArgsConstructor 30 | @NoArgsConstructor 31 | public static class Result { 32 | private String url; 33 | private String title; 34 | private String content; 35 | //@JsonProperty("parsed_url") 36 | //private List parsedUrl; 37 | //private Date publishedDate; 38 | //private List engines; 39 | //private String category; 40 | //private float score; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/service/Configuration.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.service; 2 | 3 | import io.github.lnyocly.ai4j.config.*; 4 | import io.github.lnyocly.ai4j.websearch.searxng.SearXNGConfig; 5 | import lombok.Data; 6 | import okhttp3.OkHttpClient; 7 | import okhttp3.sse.EventSource; 8 | import okhttp3.sse.EventSources; 9 | 10 | 11 | /** 12 | * @Author cly 13 | * @Description 统一的配置管理 14 | * @Date 2024/8/8 23:44 15 | */ 16 | 17 | @Data 18 | public class Configuration { 19 | 20 | private OkHttpClient okHttpClient; 21 | 22 | public EventSource.Factory createRequestFactory() { 23 | return EventSources.createFactory(okHttpClient); 24 | } 25 | 26 | private OpenAiConfig openAiConfig; 27 | private ZhipuConfig zhipuConfig; 28 | private DeepSeekConfig deepSeekConfig; 29 | private MoonshotConfig moonshotConfig; 30 | private HunyuanConfig hunyuanConfig; 31 | private LingyiConfig lingyiConfig; 32 | private OllamaConfig ollamaConfig; 33 | private MinimaxConfig minimaxConfig; 34 | private BaichuanConfig baichuanConfig; 35 | 36 | private PineconeConfig pineconeConfig; 37 | 38 | private SearXNGConfig searXNGConfig; 39 | } 40 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/chat/entity/Choice.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import lombok.Data; 7 | 8 | /** 9 | * @Author cly 10 | * @Description 模型生成的 completion 11 | * @Date 2024/8/11 20:01 12 | */ 13 | @Data 14 | @JsonIgnoreProperties(ignoreUnknown = true) 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | public class Choice { 17 | private Integer index; 18 | 19 | private ChatMessage delta; 20 | private ChatMessage message; 21 | 22 | private Object logprobs; 23 | 24 | /** 25 | * 模型停止生成 token 的原因。 26 | * 27 | * [stop, length, content_filter, tool_calls, insufficient_system_resource] 28 | * 29 | * stop:模型自然停止生成,或遇到 stop 序列中列出的字符串。 30 | * length:输出长度达到了模型上下文长度限制,或达到了 max_tokens 的限制。 31 | * content_filter:输出内容因触发过滤策略而被过滤。 32 | * tool_calls:函数调用。 33 | * insufficient_system_resource:系统推理资源不足,生成被打断。 34 | * 35 | */ 36 | @JsonProperty("finish_reason") 37 | private String finishReason; 38 | } 39 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/tools/QueryTrainInfoFunction.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.tools; 2 | 3 | import io.github.lnyocly.ai4j.annotation.FunctionParameter; 4 | import io.github.lnyocly.ai4j.annotation.FunctionCall; 5 | import io.github.lnyocly.ai4j.annotation.FunctionRequest; 6 | import lombok.Data; 7 | 8 | import java.util.function.Function; 9 | 10 | /** 11 | * @Author cly 12 | * @Description TODO 13 | * @Date 2024/8/12 14:45 14 | */ 15 | @FunctionCall(name = "queryTrainInfo", description = "查询火车是否发车信息") 16 | public class QueryTrainInfoFunction implements Function { 17 | 18 | @Data 19 | @FunctionRequest 20 | public static class Request { 21 | @FunctionParameter(description = "根据天气的情况进行查询是否发车,此参数为天气的最高气温") 22 | Integer type; 23 | } 24 | public enum Type{ 25 | hao, 26 | cha 27 | } 28 | 29 | @Override 30 | public String apply(Request request) { 31 | if (request.type > 35) { 32 | return "天气情况正常,允许发车"; 33 | } 34 | return "天气情况较差,不允许发车"; 35 | } 36 | @Data 37 | public static class Response { 38 | String orderId; 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/audio/entity/Segment.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.audio.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @Author cly 15 | * @Description Segment块 16 | * @Date 2024/10/11 11:34 17 | */ 18 | @Data 19 | @AllArgsConstructor 20 | @NoArgsConstructor 21 | @JsonInclude(JsonInclude.Include.NON_NULL) 22 | @JsonIgnoreProperties(ignoreUnknown = true) 23 | public class Segment { 24 | private Integer id; 25 | private Integer seek; 26 | private Double start; 27 | private Double end; 28 | private String text; 29 | private List tokens; 30 | private Float temperature; 31 | @JsonProperty("avg_logprob") 32 | private Double avgLogprob; 33 | @JsonProperty("compression_ratio") 34 | private Double compressionRatio; 35 | @JsonProperty("no_speech_prob") 36 | private Double noSpeechProb; 37 | } 38 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/realtime/entity/Session.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.realtime.entity; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @Author cly 7 | * @Description TODO 8 | * @Date 2024/10/12 17:24 9 | */ 10 | public class Session { 11 | /** 12 | * 模型可以响应的一组模式。要禁用音频,请将其设置为 [“text”]。 13 | */ 14 | private List modalities; 15 | 16 | /** 17 | * 默认系统指令添加到模型调用之前。 18 | */ 19 | private String instructions; 20 | 21 | /** 22 | * 模型用于响应的声音 - alloy 、 echo或shimmer之一。一旦模型至少响应一次音频,就无法更改。 23 | */ 24 | private String voice; 25 | 26 | /** 27 | * 输入音频的格式。选项为“pcm16”、“g711_ulaw”或“g711_alaw”。 28 | */ 29 | private String input_audio_format; 30 | 31 | /** 32 | * 输出音频的格式。选项为“pcm16”、“g711_ulaw”或“g711_alaw”。 33 | */ 34 | private String output_audio_format; 35 | 36 | 37 | /** 38 | * 输入音频转录的配置。可以设置为null来关闭。 39 | */ 40 | private Object input_audio_transcription; 41 | 42 | 43 | /** 44 | * 转弯检测的配置。可以设置为null来关闭。 45 | */ 46 | private Object turn_detection; 47 | 48 | 49 | private Object tools; 50 | 51 | private String tool_choice; 52 | private Double temperature; 53 | private Integer max_output_tokens; 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/exception/chain/impl/OpenAiErrorHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.exception.chain.impl; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import io.github.lnyocly.ai4j.exception.CommonException; 5 | import io.github.lnyocly.ai4j.exception.chain.AbstractErrorHandler; 6 | import io.github.lnyocly.ai4j.exception.error.Error; 7 | import io.github.lnyocly.ai4j.exception.error.OpenAiError; 8 | import org.apache.commons.lang3.ObjectUtils; 9 | 10 | /** 11 | * @Author cly 12 | * @Description OpenAi错误处理 13 | * 14 | * [openai, zhipu, deepseek, lingyi, moonshot] 错误返回类似,这里共用一个处理类 15 | * 16 | * @Date 2024/9/18 21:01 17 | */ 18 | public class OpenAiErrorHandler extends AbstractErrorHandler { 19 | 20 | @Override 21 | public Error parseError(String errorInfo) { 22 | // 解析json字符串 23 | try{ 24 | OpenAiError openAiError = JSON.parseObject(errorInfo, OpenAiError.class); 25 | 26 | Error error = openAiError.getError(); 27 | if(ObjectUtils.isEmpty(error)){ 28 | // 交给下一个节点处理 29 | return nextHandler.parseError(errorInfo); 30 | } 31 | return error; 32 | }catch (Exception e){ 33 | throw new CommonException(errorInfo); 34 | } 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/vector/pinecone/PineconeQuery.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.vector.pinecone; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | import lombok.NonNull; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * @Author cly 12 | * @Description TODO 13 | * @Date 2024/8/14 23:59 14 | */ 15 | 16 | @Data 17 | @Builder 18 | public class PineconeQuery { 19 | 20 | /** 21 | * 命名空间 22 | */ 23 | @NonNull 24 | private String namespace; 25 | 26 | /** 27 | * 需要最相似的前K条向量 28 | */ 29 | @Builder.Default 30 | private Integer topK = 10; 31 | 32 | /** 33 | * 可用于对metadata进行过滤 34 | */ 35 | private Map filter; 36 | 37 | /** 38 | * 指示响应中是否包含向量值 39 | 40 | */ 41 | @Builder.Default 42 | private Boolean includeValues = true; 43 | 44 | /** 45 | * 指示响应中是否包含元数据以及id 46 | */ 47 | @Builder.Default 48 | private Boolean includeMetadata = true; 49 | 50 | /** 51 | * 查询向量 52 | */ 53 | @NonNull 54 | private List vector; 55 | 56 | /** 57 | * 向量稀疏数据。表示为索引列表和对应值列表,它们必须具有相同的长度 58 | */ 59 | private Map sparseVector; 60 | 61 | /** 62 | * 每条向量独一无二的id 63 | */ 64 | private String id; 65 | } 66 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/utils/ValidateUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.utils; 2 | 3 | /** 4 | * @Author cly 5 | * @Description 用于验证、处理 6 | * @Date 2024/9/19 14:40 7 | */ 8 | public class ValidateUtil { 9 | 10 | public static String concatUrl(String... params){ 11 | if(params.length == 0) { 12 | throw new IllegalArgumentException("url params is empty"); 13 | } 14 | 15 | // 拼接字符串 16 | StringBuilder sb = new StringBuilder(); 17 | for (int i = 0; i < params.length; i++) { 18 | if (params[i].startsWith("/")) { 19 | params[i] = params[i].substring(1); 20 | } 21 | if(params[i].startsWith("?") || params[i].startsWith("&")){ 22 | // 如果sb的末尾是“/”,则删除末尾 23 | if(sb.length() > 0 && sb.charAt(sb.length()-1) == '/') { 24 | sb.deleteCharAt(sb.length() - 1); 25 | } 26 | 27 | } 28 | sb.append(params[i]); 29 | if(!params[i].endsWith("/")){ 30 | sb.append('/'); 31 | } 32 | } 33 | 34 | // 去掉最后一个/ 35 | if(sb.length() > 0 && sb.charAt(sb.length()-1) == '/'){ 36 | sb.deleteCharAt(sb.length()-1); 37 | } 38 | return sb.toString(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/ollama/chat/entity/OllamaChatCompletion.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.ollama.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatMessage; 7 | import io.github.lnyocly.ai4j.platform.openai.tool.Tool; 8 | import lombok.*; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @Author cly 14 | * @Description Ollama对话请求实体 15 | * @Date 2024/9/20 0:19 16 | */ 17 | @Data 18 | @Builder(toBuilder = true) 19 | @NoArgsConstructor() 20 | @AllArgsConstructor() 21 | @JsonIgnoreProperties(ignoreUnknown = true) 22 | @JsonInclude(JsonInclude.Include.NON_NULL) 23 | public class OllamaChatCompletion { 24 | 25 | @NonNull 26 | private String model; 27 | 28 | @NonNull 29 | private List messages; 30 | 31 | /** 32 | * 模型可能会调用的 tool 的列表。目前,仅支持 function 作为工具。使用此参数来提供以 JSON 作为输入参数的 function 列表。 33 | * 使用tools时,需要设置stream为false 34 | */ 35 | private List tools; 36 | 37 | /** 38 | * 辅助属性 39 | */ 40 | @JsonIgnore 41 | private List functions; 42 | 43 | private OllamaOptions options; 44 | 45 | private Boolean stream; 46 | } 47 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.lnyo-cly 8 | ai4j-sdk 9 | 10 | 0.8.1 11 | 12 | pom 13 | 14 | 15 | 16 | ai4j 17 | ai4j-spring-boot-starter 18 | 19 | 20 | 21 | validate 22 | 23 | 24 | org.apache.maven.plugins 25 | maven-deploy-plugin 26 | 27 | true 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-install-plugin 33 | 34 | true 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/lingyi/chat/entity/LingyiChatCompletionResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.lingyi.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.Choice; 7 | import io.github.lnyocly.ai4j.platform.openai.usage.Usage; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @Author cly 16 | * @Description 零一万物对话响应实体 17 | * @Date 2024/9/9 23:02 18 | */ 19 | @Data 20 | @NoArgsConstructor() 21 | @AllArgsConstructor() 22 | @JsonIgnoreProperties(ignoreUnknown = true) 23 | @JsonInclude(JsonInclude.Include.NON_NULL) 24 | public class LingyiChatCompletionResponse { 25 | /** 26 | * 该对话的唯一标识符。 27 | */ 28 | private String id; 29 | 30 | /** 31 | * 对象的类型, 其值为 chat.completion 或 chat.completion.chunk 32 | */ 33 | private String object; 34 | 35 | /** 36 | * 创建聊天完成时的 Unix 时间戳(以秒为单位)。 37 | */ 38 | private Long created; 39 | 40 | /** 41 | * 生成该 completion 的模型名。 42 | */ 43 | private String model; 44 | 45 | /** 46 | * 模型生成的 completion 的选择列表。 47 | */ 48 | private List choices; 49 | 50 | /** 51 | * 该对话补全请求的用量信息。 52 | */ 53 | private Usage usage; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/ollama/chat/entity/OllamaChatCompletionResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.ollama.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | /** 11 | * @Author cly 12 | * @Description ollama对话响应实体 13 | * @Date 2024/9/20 0:03 14 | */ 15 | @Data 16 | @NoArgsConstructor() 17 | @AllArgsConstructor() 18 | @JsonIgnoreProperties(ignoreUnknown = true) 19 | @JsonInclude(JsonInclude.Include.NON_NULL) 20 | public class OllamaChatCompletionResponse { 21 | 22 | private String model; 23 | 24 | @JsonProperty("created_at") 25 | private String createdAt; 26 | 27 | private OllamaMessage message; 28 | 29 | @JsonProperty("done_reason") 30 | private String doneReason; 31 | 32 | private Boolean done; 33 | 34 | @JsonProperty("total_duration") 35 | private long totalDuration; 36 | 37 | @JsonProperty("load_duration") 38 | private long loadDuration; 39 | 40 | @JsonProperty("prompt_eval_count") 41 | private long promptEvalCount; 42 | 43 | @JsonProperty("prompt_eval_duration") 44 | private long promptEvalDuration; 45 | 46 | @JsonProperty("eval_count") 47 | private long evalCount; 48 | 49 | @JsonProperty("eval_duration") 50 | private long evalDuration; 51 | } 52 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/minimax/chat/entity/MinimaxChatCompletionResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.minimax.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.Choice; 7 | import io.github.lnyocly.ai4j.platform.openai.usage.Usage; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @Author : isxuwl 16 | * @Date: 2024/10/15 16:24 17 | * @Model Description: 18 | * @Description: Minimax对话响应实体 19 | */ 20 | @Data 21 | @NoArgsConstructor() 22 | @AllArgsConstructor() 23 | @JsonIgnoreProperties(ignoreUnknown = true) 24 | @JsonInclude(JsonInclude.Include.NON_NULL) 25 | public class MinimaxChatCompletionResponse { 26 | /** 27 | * 该对话的唯一标识符。 28 | */ 29 | private String id; 30 | 31 | /** 32 | * 对象的类型, 其值为 chat.completion 或 chat.completion.chunk 33 | */ 34 | private String object; 35 | 36 | /** 37 | * 创建聊天完成时的 Unix 时间戳(以秒为单位)。 38 | */ 39 | private Long created; 40 | 41 | /** 42 | * 生成该 completion 的模型名。 43 | */ 44 | private String model; 45 | 46 | /** 47 | * 模型生成的 completion 的选择列表。 48 | */ 49 | private List choices; 50 | 51 | /** 52 | * 该对话补全请求的用量信息。 53 | */ 54 | private Usage usage; 55 | 56 | } 57 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/hunyuan/chat/entity/HunyuanChatCompletionResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.hunyuan.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.Choice; 7 | import io.github.lnyocly.ai4j.platform.openai.usage.Usage; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @Author cly 16 | * @Description 腾讯混元响应实体类 17 | * @Date 2024/8/30 19:27 18 | */ 19 | 20 | @Data 21 | @NoArgsConstructor() 22 | @AllArgsConstructor() 23 | @JsonIgnoreProperties(ignoreUnknown = true) 24 | @JsonInclude(JsonInclude.Include.NON_NULL) 25 | public class HunyuanChatCompletionResponse { 26 | /** 27 | * Unix 时间戳,单位为秒。 28 | */ 29 | private String created; 30 | 31 | /** 32 | * Token 统计信息。 33 | * 按照总 Token 数量计费。 34 | */ 35 | private Usage usage; 36 | 37 | /** 38 | * 免责声明。 39 | * 示例值:以上内容为AI生成,不代表开发者立场,请勿删除或修改本标记 40 | */ 41 | private String note; 42 | 43 | /** 44 | * 本次请求的 RequestId。 45 | */ 46 | private String id; 47 | 48 | /** 49 | * 回复内容 50 | */ 51 | private List choices; 52 | 53 | @JsonProperty("request_id") 54 | private String requestId; 55 | 56 | // 下面为额外补充 57 | private String object; 58 | private String model; 59 | } 60 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/chat/entity/ChatCompletionResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import io.github.lnyocly.ai4j.platform.openai.usage.Usage; 7 | import lombok.AccessLevel; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @Author cly 16 | * @Description TODO 17 | * @Date 2024/8/11 19:45 18 | */ 19 | 20 | @Data 21 | @NoArgsConstructor() 22 | @AllArgsConstructor() 23 | @JsonIgnoreProperties(ignoreUnknown = true) 24 | @JsonInclude(JsonInclude.Include.NON_NULL) 25 | public class ChatCompletionResponse { 26 | /** 27 | * 该对话的唯一标识符。 28 | */ 29 | private String id; 30 | 31 | /** 32 | * 对象的类型, 其值为 chat.completion 或 chat.completion.chunk 33 | */ 34 | private String object; 35 | 36 | /** 37 | * 创建聊天完成时的 Unix 时间戳(以秒为单位)。 38 | */ 39 | private Long created; 40 | 41 | /** 42 | * 生成该 completion 的模型名。 43 | */ 44 | private String model; 45 | 46 | /** 47 | * 该指纹代表模型运行时使用的后端配置。 48 | */ 49 | @JsonProperty("system_fingerprint") 50 | private String systemFingerprint; 51 | 52 | /** 53 | * 模型生成的 completion 的选择列表。 54 | */ 55 | private List choices; 56 | 57 | /** 58 | * 该对话补全请求的用量信息。 59 | */ 60 | private Usage usage; 61 | } 62 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/exception/chain/impl/HunyuanErrorHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.exception.chain.impl; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import io.github.lnyocly.ai4j.exception.CommonException; 5 | import io.github.lnyocly.ai4j.exception.chain.AbstractErrorHandler; 6 | import io.github.lnyocly.ai4j.exception.error.Error; 7 | import io.github.lnyocly.ai4j.exception.error.HunyuanError; 8 | import io.github.lnyocly.ai4j.exception.error.OpenAiError; 9 | import org.apache.commons.lang3.ObjectUtils; 10 | 11 | /** 12 | * @Author cly 13 | * @Description 混元错误处理 14 | * @Date 2024/9/18 23:59 15 | */ 16 | public class HunyuanErrorHandler extends AbstractErrorHandler { 17 | @Override 18 | public Error parseError(String errorInfo) { 19 | // 解析json字符串 20 | try{ 21 | HunyuanError hunyuanError = JSON.parseObject(errorInfo, HunyuanError.class); 22 | 23 | HunyuanError.Response response = hunyuanError.getResponse(); 24 | 25 | if(ObjectUtils.isEmpty(response)){ 26 | // 交给下一个节点处理 27 | return nextHandler.parseError(errorInfo); 28 | } 29 | 30 | HunyuanError.Response.Error error = response.getError(); 31 | if(ObjectUtils.isEmpty(error)){ 32 | // 交给下一个节点处理 33 | return nextHandler.parseError(errorInfo); 34 | } 35 | 36 | return new Error(error.getMessage(),error.getCode(),null,error.getCode()); 37 | }catch (Exception e){ 38 | throw new CommonException(errorInfo); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/audio/entity/TextToSpeech.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.audio.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import io.github.lnyocly.ai4j.platform.openai.audio.enums.AudioEnum; 7 | import lombok.*; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * @Author cly 13 | * @Description TextToSpeech请求实体类 14 | * @Date 2024/10/10 23:45 15 | */ 16 | @Data 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | @Builder 20 | @JsonInclude(JsonInclude.Include.NON_NULL) 21 | @JsonIgnoreProperties(ignoreUnknown = true) 22 | public class TextToSpeech { 23 | /** 24 | * tts-1 or tts-1-hd 25 | */ 26 | @Builder.Default 27 | @NonNull 28 | private String model = "tts-1"; 29 | 30 | /** 31 | * 要为其生成音频的文本。最大长度为 4096 个字符。 32 | */ 33 | @NonNull 34 | private String input; 35 | 36 | 37 | /** 38 | * 生成音频时要使用的语音。支持的声音包括 alloy、echo、fable、onyx、nova 和 shimmer 39 | */ 40 | @Builder.Default 41 | @NonNull 42 | private String voice = AudioEnum.Voice.ALLOY.getValue(); 43 | 44 | /** 45 | * 音频输入的格式。支持的格式包括 mp3, opus, aac, flac, wav, and pcm。 46 | */ 47 | @Builder.Default 48 | @JsonProperty("response_format") 49 | private String responseFormat = AudioEnum.ResponseFormat.MP3.getValue(); 50 | 51 | /** 52 | * 生成的音频的速度。选择一个介于 0.25 到 4.0 之间的值。默认值为 1.0。 53 | */ 54 | @Builder.Default 55 | private Double speed = 1.0d; 56 | } -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/deepseek/chat/entity/DeepSeekChatCompletionResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.deepseek.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.Choice; 7 | import io.github.lnyocly.ai4j.platform.openai.usage.Usage; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @Author cly 16 | * @Description DeepSeek对话响应实体 17 | * @Date 2024/8/29 10:28 18 | */ 19 | 20 | @Data 21 | @NoArgsConstructor() 22 | @AllArgsConstructor() 23 | @JsonIgnoreProperties(ignoreUnknown = true) 24 | @JsonInclude(JsonInclude.Include.NON_NULL) 25 | public class DeepSeekChatCompletionResponse { 26 | /** 27 | * 该对话的唯一标识符。 28 | */ 29 | private String id; 30 | 31 | /** 32 | * 对象的类型, 其值为 chat.completion 或 chat.completion.chunk 33 | */ 34 | private String object; 35 | 36 | /** 37 | * 创建聊天完成时的 Unix 时间戳(以秒为单位)。 38 | */ 39 | private Long created; 40 | 41 | /** 42 | * 生成该 completion 的模型名。 43 | */ 44 | private String model; 45 | 46 | /** 47 | * 模型生成的 completion 的选择列表。 48 | */ 49 | private List choices; 50 | 51 | /** 52 | * 该对话补全请求的用量信息。 53 | */ 54 | private Usage usage; 55 | 56 | /** 57 | * 该指纹代表模型运行时使用的后端配置。 58 | */ 59 | @JsonProperty("system_fingerprint") 60 | private String systemFingerprint; 61 | } 62 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/moonshot/chat/entity/MoonshotChatCompletionResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.moonshot.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.Choice; 7 | import io.github.lnyocly.ai4j.platform.openai.usage.Usage; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @Author cly 16 | * @Description 月之暗面对话响应实体 17 | * @Date 2024/8/29 10:28 18 | */ 19 | 20 | @Data 21 | @NoArgsConstructor() 22 | @AllArgsConstructor() 23 | @JsonIgnoreProperties(ignoreUnknown = true) 24 | @JsonInclude(JsonInclude.Include.NON_NULL) 25 | public class MoonshotChatCompletionResponse { 26 | /** 27 | * 该对话的唯一标识符。 28 | */ 29 | private String id; 30 | 31 | /** 32 | * 对象的类型, 其值为 chat.completion 或 chat.completion.chunk 33 | */ 34 | private String object; 35 | 36 | /** 37 | * 创建聊天完成时的 Unix 时间戳(以秒为单位)。 38 | */ 39 | private Long created; 40 | 41 | /** 42 | * 生成该 completion 的模型名。 43 | */ 44 | private String model; 45 | 46 | /** 47 | * 模型生成的 completion 的选择列表。 48 | */ 49 | private List choices; 50 | 51 | /** 52 | * 该对话补全请求的用量信息。 53 | */ 54 | private Usage usage; 55 | 56 | /** 57 | * 该指纹代表模型运行时使用的后端配置。 58 | */ 59 | @JsonProperty("system_fingerprint") 60 | private String systemFingerprint; 61 | } 62 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/exception/chain/ErrorHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.exception.chain; 2 | 3 | import io.github.lnyocly.ai4j.exception.chain.impl.HunyuanErrorHandler; 4 | import io.github.lnyocly.ai4j.exception.chain.impl.OpenAiErrorHandler; 5 | import io.github.lnyocly.ai4j.exception.chain.impl.UnknownErrorHandler; 6 | import io.github.lnyocly.ai4j.exception.error.Error; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * @Author cly 13 | * @Description 创建错误处理的单例 14 | * @Date 2024/9/18 21:09 15 | */ 16 | public class ErrorHandler { 17 | private List handlers; 18 | private IErrorHandler chain; 19 | 20 | private ErrorHandler() { 21 | handlers = new ArrayList<>(); 22 | // 添加错误处理器 23 | handlers.add(new OpenAiErrorHandler()); 24 | handlers.add(new HunyuanErrorHandler()); 25 | 26 | // 兜底的错误处理 27 | handlers.add(new UnknownErrorHandler()); 28 | 29 | // 组装链 30 | this.assembleChain(); 31 | } 32 | 33 | private void assembleChain(){ 34 | chain = handlers.get(0); 35 | IErrorHandler curr = handlers.get(0); 36 | for (int i = 1; i < handlers.size(); i++) { 37 | curr.setNext(handlers.get(i)); 38 | curr = handlers.get(i); 39 | } 40 | } 41 | 42 | private static class ErrorHandlerHolder { 43 | private static final ErrorHandler INSTANCE = new ErrorHandler(); 44 | } 45 | 46 | public static ErrorHandler getInstance() { 47 | return ErrorHandlerHolder.INSTANCE; 48 | } 49 | 50 | public Error process(String errorSring){ 51 | return chain.parseError(errorSring); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/embedding/entity/Embedding.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.embedding.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import lombok.*; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @Author cly 12 | * @Description Embedding 实体类 13 | * @Date 2024/8/7 17:20 14 | */ 15 | @Data 16 | @Builder 17 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 18 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 19 | @JsonIgnoreProperties(ignoreUnknown = true) 20 | @JsonInclude(JsonInclude.Include.NON_NULL) 21 | public class Embedding { 22 | /** 23 | * 向量化文本 24 | */ 25 | @NonNull 26 | private Object input; 27 | 28 | /** 29 | * 向量模型 30 | */ 31 | @NonNull 32 | @Builder.Default 33 | private String model = "text-embedding-3-small"; 34 | @JsonProperty("encoding_format") 35 | private String encodingFormat; 36 | 37 | /** 38 | * 向量维度 建议选择256、512、1024或2048维度 39 | */ 40 | private String dimensions; 41 | private String user; 42 | 43 | public static class EmbeddingBuilder { 44 | private Object input; 45 | private Embedding.EmbeddingBuilder input(Object input){ 46 | this.input = input; 47 | return this; 48 | } 49 | 50 | public Embedding.EmbeddingBuilder input(String input){ 51 | this.input = input; 52 | return this; 53 | } 54 | 55 | public Embedding.EmbeddingBuilder input(List content){ 56 | this.input = content; 57 | return this; 58 | } 59 | 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/utils/OkHttpUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.utils; 2 | 3 | import javax.net.ssl.*; 4 | import java.security.KeyManagementException; 5 | import java.security.NoSuchAlgorithmException; 6 | import java.security.SecureRandom; 7 | import java.security.cert.X509Certificate; 8 | 9 | /** 10 | * 11 | * @author Vania 12 | * 13 | */ 14 | public class OkHttpUtil { 15 | /** 16 | * X509TrustManager instance which ignored SSL certification 17 | */ 18 | public static final X509TrustManager IGNORE_SSL_TRUST_MANAGER_X509 = new X509TrustManager() { 19 | @Override 20 | public void checkClientTrusted(X509Certificate[] chain, String authType) { 21 | } 22 | 23 | @Override 24 | public void checkServerTrusted(X509Certificate[] chain, String authType) { 25 | } 26 | 27 | @Override 28 | public X509Certificate[] getAcceptedIssuers() { 29 | return new X509Certificate[] {}; 30 | } 31 | }; 32 | 33 | /** 34 | * Get initialized SSLContext instance which ignored SSL certification 35 | * 36 | * @return 37 | * @throws NoSuchAlgorithmException 38 | * @throws KeyManagementException 39 | */ 40 | public static SSLContext getIgnoreInitedSslContext() throws NoSuchAlgorithmException, KeyManagementException { 41 | SSLContext sslContext = SSLContext.getInstance("SSL"); 42 | sslContext.init(null, new TrustManager[] { IGNORE_SSL_TRUST_MANAGER_X509 }, new SecureRandom()); 43 | return sslContext; 44 | } 45 | 46 | /** 47 | * Get HostnameVerifier which ignored SSL certification 48 | * 49 | * @return 50 | */ 51 | public static HostnameVerifier getIgnoreSslHostnameVerifier() { 52 | return new HostnameVerifier() { 53 | @Override 54 | public boolean verify(String arg0, SSLSession arg1) { 55 | return true; 56 | } 57 | }; 58 | } 59 | } -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/listener/RealtimeListener.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.listener; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import okhttp3.Response; 5 | import okhttp3.WebSocket; 6 | import okhttp3.WebSocketListener; 7 | import okio.ByteString; 8 | import org.apache.log4j.Logger; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | /** 13 | * @Author cly 14 | * @Description RealtimeListener, 用于统一处理WebSocket的事件(Realtime客户端专用) 15 | * @Date 2024/10/12 16:33 16 | */ 17 | @Slf4j 18 | public abstract class RealtimeListener extends WebSocketListener { 19 | 20 | protected abstract void onOpen(WebSocket webSocket); 21 | protected abstract void onMessage(ByteString bytes); 22 | protected abstract void onMessage(String text); 23 | protected abstract void onFailure(); 24 | 25 | @Override 26 | public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) { 27 | log.info("WebSocket Opened: " + response.message()); 28 | this.onOpen(webSocket); 29 | } 30 | 31 | @Override 32 | public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString bytes) { 33 | log.info("Receive Byte Message: " + bytes.toString()); 34 | this.onMessage(bytes); 35 | } 36 | 37 | @Override 38 | public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { 39 | log.info("Receive String Message: " + text); 40 | this.onMessage(text); 41 | } 42 | 43 | @Override 44 | public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) { 45 | log.error("WebSocket Error: ", t); 46 | } 47 | 48 | @Override 49 | public void onClosing(@NotNull WebSocket webSocket, int code, @NotNull String reason) { 50 | } 51 | 52 | @Override 53 | public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) { 54 | log.info("WebSocket Closed: " + reason); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/realtime/OpenAiRealtimeService.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.realtime; 2 | 3 | import io.github.lnyocly.ai4j.config.OpenAiConfig; 4 | import io.github.lnyocly.ai4j.listener.RealtimeListener; 5 | import io.github.lnyocly.ai4j.service.Configuration; 6 | import io.github.lnyocly.ai4j.service.IRealtimeService; 7 | import io.github.lnyocly.ai4j.utils.ValidateUtil; 8 | import okhttp3.OkHttpClient; 9 | import okhttp3.Request; 10 | import okhttp3.WebSocket; 11 | 12 | /** 13 | * @Author cly 14 | * @Description OpenAiRealtimeService 15 | * @Date 2024/10/12 16:39 16 | */ 17 | public class OpenAiRealtimeService implements IRealtimeService { 18 | private final OpenAiConfig openAiConfig; 19 | private final OkHttpClient okHttpClient; 20 | 21 | public OpenAiRealtimeService(Configuration configuration) { 22 | this.openAiConfig = configuration.getOpenAiConfig(); 23 | this.okHttpClient = configuration.getOkHttpClient(); 24 | } 25 | 26 | 27 | @Override 28 | public WebSocket createRealtimeClient(String baseUrl, String apiKey, String model, RealtimeListener realtimeListener) { 29 | if(baseUrl == null || "".equals(baseUrl)) baseUrl = openAiConfig.getApiHost(); // url为HTTPS不影响 30 | if(apiKey == null || "".equals(apiKey)) apiKey = openAiConfig.getApiKey(); 31 | 32 | String url = ValidateUtil.concatUrl(baseUrl, openAiConfig.getRealtimeUrl(), "?model=" + model); 33 | Request request = new Request.Builder() 34 | .url(url) 35 | .addHeader("Authorization", "Bearer " + apiKey) 36 | .addHeader("OpenAI-Beta", "realtime=v1") 37 | .build(); 38 | return okHttpClient.newWebSocket(request, realtimeListener); 39 | 40 | } 41 | 42 | @Override 43 | public WebSocket createRealtimeClient(String model, RealtimeListener realtimeListener) { 44 | return this.createRealtimeClient(null, null, model, realtimeListener); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/embedding/OpenAiEmbeddingService.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.embedding; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import io.github.lnyocly.ai4j.config.OpenAiConfig; 5 | import io.github.lnyocly.ai4j.constant.Constants; 6 | import io.github.lnyocly.ai4j.platform.openai.embedding.entity.Embedding; 7 | import io.github.lnyocly.ai4j.platform.openai.embedding.entity.EmbeddingResponse; 8 | import io.github.lnyocly.ai4j.service.Configuration; 9 | import io.github.lnyocly.ai4j.service.IEmbeddingService; 10 | import io.github.lnyocly.ai4j.utils.ValidateUtil; 11 | import okhttp3.*; 12 | 13 | /** 14 | * @Author cly 15 | * @Description TODO 16 | * @Date 2024/8/7 17:40 17 | */ 18 | public class OpenAiEmbeddingService implements IEmbeddingService { 19 | 20 | private final OpenAiConfig openAiConfig; 21 | private final OkHttpClient okHttpClient; 22 | 23 | public OpenAiEmbeddingService(Configuration configuration) { 24 | this.openAiConfig = configuration.getOpenAiConfig(); 25 | this.okHttpClient = configuration.getOkHttpClient(); 26 | } 27 | 28 | 29 | @Override 30 | public EmbeddingResponse embedding(String baseUrl, String apiKey, Embedding embeddingReq) throws Exception { 31 | if(baseUrl == null || "".equals(baseUrl)) baseUrl = openAiConfig.getApiHost(); 32 | if(apiKey == null || "".equals(apiKey)) apiKey = openAiConfig.getApiKey(); 33 | String jsonString = JSON.toJSONString(embeddingReq); 34 | 35 | Request request = new Request.Builder() 36 | .header("Authorization", "Bearer " + apiKey) 37 | .url(ValidateUtil.concatUrl(baseUrl, openAiConfig.getEmbeddingUrl())) 38 | .post(RequestBody.create(MediaType.parse(Constants.APPLICATION_JSON), jsonString)) 39 | .build(); 40 | Response execute = okHttpClient.newCall(request).execute(); 41 | if (execute.isSuccessful() && execute.body() != null) { 42 | return JSON.parseObject(execute.body().string(), EmbeddingResponse.class); 43 | } 44 | return null; 45 | } 46 | 47 | @Override 48 | public EmbeddingResponse embedding(Embedding embeddingReq) throws Exception { 49 | return embedding(null, null, embeddingReq); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/interceptor/ContentTypeInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.interceptor; 2 | 3 | import okhttp3.Interceptor; 4 | import okhttp3.MediaType; 5 | import okhttp3.Response; 6 | import okhttp3.ResponseBody; 7 | import okio.Buffer; 8 | import okio.BufferedSource; 9 | 10 | import java.io.IOException; 11 | import java.nio.charset.StandardCharsets; 12 | 13 | /** 14 | * @Author cly 15 | * @Description TODO 16 | * @Date 2024/9/20 18:56 17 | */ 18 | public class ContentTypeInterceptor implements Interceptor { 19 | 20 | @Override 21 | public Response intercept(Chain chain) throws IOException { 22 | // 发起请求并获取响应 23 | Response response = chain.proceed(chain.request()); 24 | 25 | // 检查Content-Type是否为application/x-ndjson 26 | if (response.header("Content-Type") != null && 27 | response.header("Content-Type").contains("application/x-ndjson")) { 28 | 29 | // 获取原始响应体 30 | ResponseBody responseBody = response.body(); 31 | BufferedSource source = responseBody.source(); 32 | source.request(Long.MAX_VALUE); // 缓冲整个响应体 33 | Buffer buffer = source.getBuffer(); 34 | 35 | // 读取响应体并将其按换行符分割,模拟处理 application/x-ndjson -> text/event-stream 36 | String bodyString = buffer.clone().readString(StandardCharsets.UTF_8); 37 | String[] ndjsonLines = bodyString.split("\n"); 38 | 39 | StringBuilder sseBody = new StringBuilder(); 40 | for (String jsonLine : ndjsonLines) { 41 | if (!jsonLine.trim().isEmpty()) { 42 | // 这里简单处理,将ndjson的每一行当作SSE事件的data部分 43 | sseBody.append("data: ").append(jsonLine).append("\n\n"); 44 | } 45 | } 46 | 47 | // 创建新的响应体,替换掉原有的内容 48 | ResponseBody modifiedBody = ResponseBody.create( 49 | MediaType.get("text/event-stream"), 50 | sseBody.toString() 51 | ); 52 | 53 | // 返回修改后的响应,更新了Content-Type和响应体 54 | return response.newBuilder() 55 | .header("Content-Type", "text/event-stream") 56 | .body(modifiedBody) 57 | .build(); 58 | } 59 | 60 | return response; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/lingyi/chat/entity/LingyiChatCompletion.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.lingyi.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatMessage; 8 | import io.github.lnyocly.ai4j.platform.openai.tool.Tool; 9 | import lombok.*; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @Author cly 15 | * @Description 零一万物对话请求实体 16 | * @Date 2024/9/9 23:01 17 | */ 18 | @Data 19 | @Builder(toBuilder = true) 20 | @NoArgsConstructor() 21 | @AllArgsConstructor() 22 | @JsonIgnoreProperties(ignoreUnknown = true) 23 | @JsonInclude(JsonInclude.Include.NON_NULL) 24 | public class LingyiChatCompletion { 25 | 26 | @NonNull 27 | private String model; 28 | 29 | @NonNull 30 | private List messages; 31 | 32 | /** 33 | * 模型可能会调用的 tool 的列表。目前,仅支持 function 作为工具。使用此参数来提供以 JSON 作为输入参数的 function 列表。 34 | */ 35 | private List tools; 36 | 37 | /** 38 | * 辅助属性 39 | */ 40 | @JsonIgnore 41 | private List functions; 42 | 43 | /** 44 | * 控制模型调用 tool 的行为。 45 | * none 意味着模型不会调用任何 tool,而是生成一条消息。 46 | * auto 意味着模型可以选择生成一条消息或调用一个或多个 tool。 47 | * 当没有 tool 时,默认值为 none。如果有 tool 存在,默认值为 auto。 48 | */ 49 | @JsonProperty("tool_choice") 50 | private String toolChoice; 51 | 52 | /** 53 | * 采样温度,介于 0 和 2 之间。更高的值,如 0.8,会使输出更随机,而更低的值,如 0.2,会使其更加集中和确定。 54 | * 我们通常建议可以更改这个值或者更改 top_p,但不建议同时对两者进行修改。 55 | */ 56 | @Builder.Default 57 | private Float temperature = 1f; 58 | 59 | /** 60 | * 作为调节采样温度的替代方案,模型会考虑前 top_p 概率的 token 的结果。所以 0.1 就意味着只有包括在最高 10% 概率中的 token 会被考虑。 61 | * 我们通常建议修改这个值或者更改 temperature,但不建议同时对两者进行修改。 62 | */ 63 | @Builder.Default 64 | @JsonProperty("top_p") 65 | private Float topP = 1f; 66 | 67 | /** 68 | * 如果设置为 True,将会以 SSE(server-sent events)的形式以流式发送消息增量。消息流以 data: [DONE] 结尾 69 | */ 70 | private Boolean stream = false; 71 | 72 | 73 | /** 74 | * 限制一次请求中模型生成 completion 的最大 token 数。输入 token 和输出 token 的总长度受模型的上下文长度的限制。 75 | */ 76 | @JsonProperty("max_tokens") 77 | private Integer maxTokens; 78 | 79 | } 80 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/minimax/chat/entity/MinimaxChatCompletion.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.minimax.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatMessage; 8 | import io.github.lnyocly.ai4j.platform.openai.tool.Tool; 9 | import lombok.*; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @Author : isxuwl 15 | * @Date: 2024/10/15 16:24 16 | * @Model Description: 17 | * @Description: Minimax对话请求实体 18 | */ 19 | 20 | @Data 21 | @Builder(toBuilder = true) 22 | @NoArgsConstructor() 23 | @AllArgsConstructor() 24 | @JsonIgnoreProperties(ignoreUnknown = true) 25 | @JsonInclude(JsonInclude.Include.NON_NULL) 26 | public class MinimaxChatCompletion { 27 | 28 | @NonNull 29 | private String model; 30 | 31 | @NonNull 32 | private List messages; 33 | 34 | /** 35 | * 模型可能会调用的 tool 的列表。目前,仅支持 function 作为工具。使用此参数来提供以 JSON 作为输入参数的 function 列表。 36 | */ 37 | private List tools; 38 | 39 | /** 40 | * 辅助属性 41 | */ 42 | @JsonIgnore 43 | private List functions; 44 | 45 | /** 46 | * 控制模型调用 tool 的行为。 47 | * none 意味着模型不会调用任何 tool,而是生成一条消息。 48 | * auto 意味着模型可以选择生成一条消息或调用一个或多个 tool。 49 | * 当没有 tool 时,默认值为 none。如果有 tool 存在,默认值为 auto。 50 | */ 51 | @JsonProperty("tool_choice") 52 | private String toolChoice; 53 | 54 | /** 55 | * 采样温度,介于 0 和 2 之间。更高的值,如 0.8,会使输出更随机,而更低的值,如 0.2,会使其更加集中和确定。 56 | * 我们通常建议可以更改这个值或者更改 top_p,但不建议同时对两者进行修改。 57 | */ 58 | @Builder.Default 59 | private Float temperature = 1f; 60 | 61 | /** 62 | * 作为调节采样温度的替代方案,模型会考虑前 top_p 概率的 token 的结果。所以 0.1 就意味着只有包括在最高 10% 概率中的 token 会被考虑。 63 | * 我们通常建议修改这个值或者更改 temperature,但不建议同时对两者进行修改。 64 | */ 65 | @Builder.Default 66 | @JsonProperty("top_p") 67 | private Float topP = 1f; 68 | 69 | /** 70 | * 如果设置为 True,将会以 SSE(server-sent events)的形式以流式发送消息增量。消息流以 data: [DONE] 结尾 71 | */ 72 | private Boolean stream = false; 73 | 74 | 75 | /** 76 | * 限制一次请求中模型生成 completion 的最大 token 数。输入 token 和输出 token 的总长度受模型的上下文长度的限制。 77 | */ 78 | @JsonProperty("max_tokens") 79 | private Integer maxTokens; 80 | 81 | } 82 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/tools/QueryWeatherFunction.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.tools; 2 | 3 | import io.github.lnyocly.ai4j.annotation.FunctionCall; 4 | import io.github.lnyocly.ai4j.annotation.FunctionParameter; 5 | import io.github.lnyocly.ai4j.annotation.FunctionRequest; 6 | import io.github.lnyocly.ai4j.constant.Constants; 7 | import lombok.Data; 8 | import okhttp3.*; 9 | 10 | import java.util.function.Function; 11 | 12 | /** 13 | * @Author cly 14 | * @Description TODO 15 | * @Date 2024/8/13 17:40 16 | */ 17 | @FunctionCall(name = "queryWeather", description = "查询目标地点的天气预报") 18 | public class QueryWeatherFunction implements Function { 19 | @Override 20 | public String apply(Request request) { 21 | final String key = "S3zzVyAdJjEeB18Gw"; 22 | // https://api.seniverse.com/v3/weather/hourly.json?key=your_api_key&location=beijing&start=0&hours=24 23 | // https://api.seniverse.com/v3/weather/daily.json?key=your_api_key&location=beijing&start=0&days=5 24 | String url = String.format("https://api.seniverse.com/v3/weather/%s.json?key=%s&location=%s&days=%d", 25 | request.type.name(), 26 | key, 27 | request.location, 28 | request.days); 29 | 30 | 31 | OkHttpClient client = new OkHttpClient(); 32 | 33 | okhttp3.Request http = new okhttp3.Request.Builder() 34 | .url(url) 35 | .get() 36 | .build(); 37 | 38 | try (Response response = client.newCall(http).execute()) { 39 | if (response.isSuccessful()) { 40 | // 解析响应体 41 | return response.body() != null ? response.body().string() : ""; 42 | } else { 43 | return "获取天气失败 当前天气未知"; 44 | } 45 | } catch (Exception e) { 46 | // 处理异常 47 | e.printStackTrace(); 48 | return "获取天气失败 当前天气未知"; 49 | } 50 | } 51 | 52 | @Data 53 | @FunctionRequest 54 | public static class Request{ 55 | @FunctionParameter(description = "需要查询天气的目标位置, 可以是城市中文名、城市拼音/英文名、省市名称组合、IP 地址、经纬度") 56 | private String location; 57 | @FunctionParameter(description = "需要查询未来天气的天数, 最多15日") 58 | private int days = 15; 59 | @FunctionParameter(description = "预报的天气类型,daily表示预报多天天气、hourly表示预测当天24天气、now为当前天气实况") 60 | private Type type; 61 | } 62 | 63 | public enum Type{ 64 | daily, 65 | hourly, 66 | now 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/baichuan/chat/entity/BaichuanChatCompletion.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.baichuan.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatMessage; 8 | import io.github.lnyocly.ai4j.platform.openai.tool.Tool; 9 | import lombok.*; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | @Data 16 | @Builder(toBuilder = true) 17 | @NoArgsConstructor() 18 | @AllArgsConstructor() 19 | @JsonIgnoreProperties(ignoreUnknown = true) 20 | @JsonInclude(JsonInclude.Include.NON_NULL) 21 | public class BaichuanChatCompletion { 22 | 23 | @NonNull 24 | private String model; 25 | @NonNull 26 | private List messages; 27 | 28 | @JsonProperty("request_id") 29 | private String requestId; 30 | 31 | @JsonProperty("do_sample") 32 | private Boolean doSample = true; 33 | private Boolean stream = false; 34 | /** 35 | * 采样温度,控制输出的随机性,必须为正数。值越大,会使输出更随机 36 | * [0.0, 1.0] 37 | */ 38 | private Float temperature = 0.95f; 39 | /** 40 | * 核取样 41 | * [0.0, 1.0] 42 | */ 43 | @JsonProperty("top_p") 44 | private Float topP = 0.7f; 45 | 46 | @JsonProperty("max_tokens") 47 | private Integer maxTokens; 48 | 49 | private List stop; 50 | 51 | 52 | private List tools; 53 | 54 | /** 55 | * 辅助属性 56 | */ 57 | @JsonIgnore 58 | private List functions; 59 | 60 | @JsonProperty("tool_choice") 61 | private String toolChoice; 62 | 63 | @JsonProperty("user_id") 64 | private String userId; 65 | 66 | public static class ZhipuChatCompletionBuilder { 67 | private List functions; 68 | 69 | public ZhipuChatCompletionBuilder functions(String... functions){ 70 | if (this.functions == null) { 71 | this.functions = new ArrayList<>(); 72 | } 73 | this.functions.addAll(Arrays.asList(functions)); 74 | return this; 75 | } 76 | 77 | public ZhipuChatCompletionBuilder functions(List functions){ 78 | if (this.functions == null) { 79 | this.functions = new ArrayList<>(); 80 | } 81 | this.functions.addAll(functions); 82 | return this; 83 | } 84 | 85 | 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/tool/Tool.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.tool; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | /** 14 | * @Author cly 15 | * @Description TODO 16 | * @Date 2024/8/12 14:55 17 | */ 18 | @Data 19 | @AllArgsConstructor 20 | @NoArgsConstructor 21 | @JsonIgnoreProperties(ignoreUnknown = true) 22 | @JsonInclude(JsonInclude.Include.NON_NULL) 23 | public class Tool { 24 | 25 | /** 26 | * 工具类型,目前为“function” 27 | */ 28 | private String type; 29 | private Function function; 30 | 31 | @Data 32 | @AllArgsConstructor 33 | @NoArgsConstructor 34 | @JsonIgnoreProperties(ignoreUnknown = true) 35 | @JsonInclude(JsonInclude.Include.NON_NULL) 36 | public static class Function { 37 | 38 | /** 39 | * 函数名称 40 | */ 41 | private String name; 42 | 43 | /** 44 | * 函数描述 45 | */ 46 | private String description; 47 | 48 | /** 49 | * 函数的参数 key为参数名称,value为参数属性 50 | */ 51 | private Parameter parameters; 52 | 53 | 54 | 55 | @Data 56 | @AllArgsConstructor 57 | @NoArgsConstructor 58 | @JsonIgnoreProperties(ignoreUnknown = true) 59 | @JsonInclude(JsonInclude.Include.NON_NULL) 60 | public static class Parameter { 61 | 62 | private String type = "object"; 63 | 64 | /** 65 | * 函数的参数 key为参数名称,value为参数属性 66 | */ 67 | private Map properties; 68 | 69 | /** 70 | * 必须的参数 71 | */ 72 | private List required; 73 | 74 | } 75 | 76 | @Data 77 | @AllArgsConstructor 78 | @NoArgsConstructor 79 | @JsonIgnoreProperties(ignoreUnknown = true) 80 | @JsonInclude(JsonInclude.Include.NON_NULL) 81 | public static class Property { 82 | /** 83 | * 属性类型 84 | */ 85 | private String type; 86 | 87 | /** 88 | * 属性描述 89 | */ 90 | private String description; 91 | 92 | /** 93 | * 枚举项 94 | */ 95 | @JsonProperty("enum") 96 | private List enumValues; 97 | } 98 | 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/interceptor/ErrorInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.interceptor; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import com.alibaba.fastjson2.JSONObject; 5 | import io.github.lnyocly.ai4j.exception.CommonException; 6 | import io.github.lnyocly.ai4j.exception.chain.ErrorHandler; 7 | import io.github.lnyocly.ai4j.exception.error.Error; 8 | import lombok.extern.slf4j.Slf4j; 9 | import okhttp3.Interceptor; 10 | import okhttp3.Request; 11 | import okhttp3.Response; 12 | import okhttp3.ResponseBody; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.io.IOException; 16 | 17 | /** 18 | * @Author cly 19 | * @Description 错误处理器 20 | * @Date 2024/8/29 14:55 21 | */ 22 | @Slf4j 23 | public class ErrorInterceptor implements Interceptor { 24 | @NotNull 25 | @Override 26 | public Response intercept(@NotNull Chain chain) throws IOException { 27 | Request original = chain.request(); 28 | 29 | Response response = chain.proceed(original); 30 | 31 | byte[] contentBytes = response.body().bytes(); 32 | 33 | String errorMsg = new String(contentBytes); 34 | 35 | if(!response.isSuccessful() && (response.code() != 100 && response.code() != 101)){ 36 | JSONObject object; 37 | try { 38 | object = JSON.parseObject(errorMsg); 39 | if(object == null){ 40 | errorMsg = response.code() + " " + response.message(); 41 | throw new CommonException(errorMsg); 42 | } 43 | } catch (Exception e) { 44 | throw new CommonException(errorMsg); 45 | } 46 | // 处理错误信息 47 | ErrorHandler errorHandler = ErrorHandler.getInstance(); 48 | Error error = errorHandler.process(errorMsg); 49 | 50 | log.error("AI服务请求异常:{}", error.getMessage()); 51 | throw new CommonException(error.getMessage()); 52 | 53 | }else{ 54 | // 对混元特殊处理 55 | // {"Response":{"RequestId":"e4650694-f018-4490-b4d0-d5242cd68106","Error":{"Code":"InvalidParameterValue.Model","Message":"模型不存在"}}} 56 | 57 | if (errorMsg.contains("Response") && errorMsg.contains("Error")){ 58 | // 处理错误信息 59 | ErrorHandler errorHandler = ErrorHandler.getInstance(); 60 | Error error = errorHandler.process(errorMsg); 61 | log.error("AI服务请求异常:{}", error.getMessage()); 62 | throw new CommonException(error.getMessage()); 63 | } 64 | 65 | 66 | } 67 | ResponseBody newResponseBody = ResponseBody.create(response.body().contentType(), contentBytes); 68 | 69 | return response.newBuilder() 70 | .body(newResponseBody) 71 | .build(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/zhipu/chat/entity/ZhipuChatCompletion.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.zhipu.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatCompletion; 8 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatMessage; 9 | import io.github.lnyocly.ai4j.platform.openai.tool.Tool; 10 | import lombok.*; 11 | 12 | import java.io.Serializable; 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | 17 | /** 18 | * @Author cly 19 | * @Description 智谱对话实体类 20 | * @Date 2024/8/27 17:39 21 | */ 22 | @Data 23 | @Builder(toBuilder = true) 24 | @NoArgsConstructor() 25 | @AllArgsConstructor() 26 | @JsonIgnoreProperties(ignoreUnknown = true) 27 | @JsonInclude(JsonInclude.Include.NON_NULL) 28 | public class ZhipuChatCompletion { 29 | 30 | @NonNull 31 | private String model; 32 | @NonNull 33 | private List messages; 34 | 35 | @JsonProperty("request_id") 36 | private String requestId; 37 | 38 | @JsonProperty("do_sample") 39 | private Boolean doSample = true; 40 | private Boolean stream = false; 41 | /** 42 | * 采样温度,控制输出的随机性,必须为正数。值越大,会使输出更随机 43 | * [0.0, 1.0] 44 | */ 45 | private Float temperature = 0.95f; 46 | /** 47 | * 核取样 48 | * [0.0, 1.0] 49 | */ 50 | @JsonProperty("top_p") 51 | private Float topP = 0.7f; 52 | 53 | @JsonProperty("max_tokens") 54 | private Integer maxTokens; 55 | 56 | private List stop; 57 | 58 | 59 | private List tools; 60 | 61 | /** 62 | * 辅助属性 63 | */ 64 | @JsonIgnore 65 | private List functions; 66 | 67 | @JsonProperty("tool_choice") 68 | private String toolChoice; 69 | 70 | @JsonProperty("user_id") 71 | private String userId; 72 | 73 | public static class ZhipuChatCompletionBuilder { 74 | private List functions; 75 | 76 | public ZhipuChatCompletion.ZhipuChatCompletionBuilder functions(String... functions){ 77 | if (this.functions == null) { 78 | this.functions = new ArrayList<>(); 79 | } 80 | this.functions.addAll(Arrays.asList(functions)); 81 | return this; 82 | } 83 | 84 | public ZhipuChatCompletion.ZhipuChatCompletionBuilder functions(List functions){ 85 | if (this.functions == null) { 86 | this.functions = new ArrayList<>(); 87 | } 88 | this.functions.addAll(functions); 89 | return this; 90 | } 91 | 92 | 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/utils/TikaUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.utils; 2 | 3 | import org.apache.tika.Tika; 4 | import org.apache.tika.exception.TikaException; 5 | import org.apache.tika.metadata.Metadata; 6 | import org.apache.tika.parser.AutoDetectParser; 7 | import org.apache.tika.parser.ParseContext; 8 | import org.apache.tika.sax.BodyContentHandler; 9 | import org.xml.sax.SAXException; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | 15 | public class TikaUtil { 16 | 17 | private static final Tika tika = new Tika(); 18 | 19 | /** 20 | * 解析File文件,返回文档内容 21 | * @param file 要解析的文件 22 | * @return 解析后的文档内容 23 | * @throws IOException 24 | * @throws TikaException 25 | * @throws SAXException 26 | */ 27 | public static String parseFile(File file) throws IOException, TikaException, SAXException { 28 | try (InputStream stream = file.toURI().toURL().openStream()) { 29 | return parseInputStream(stream); 30 | } 31 | } 32 | 33 | /** 34 | * 解析InputStream输入流,返回文档内容 35 | * @param stream 要解析的输入流 36 | * @return 解析后的文档内容 37 | * @throws IOException 38 | * @throws TikaException 39 | * @throws SAXException 40 | */ 41 | public static String parseInputStream(InputStream stream) throws IOException, TikaException, SAXException { 42 | BodyContentHandler handler = new BodyContentHandler(); 43 | Metadata metadata = new Metadata(); 44 | AutoDetectParser parser = new AutoDetectParser(); 45 | ParseContext context = new ParseContext(); 46 | 47 | parser.parse(stream, handler, metadata, context); 48 | return handler.toString(); 49 | } 50 | 51 | /** 52 | * 使用Tika简单接口解析文件,返回文档内容 53 | * @param file 要解析的文件 54 | * @return 解析后的文档内容 55 | * @throws IOException 56 | * @throws TikaException 57 | */ 58 | public static String parseFileWithTika(File file) throws IOException, TikaException { 59 | return tika.parseToString(file); 60 | } 61 | 62 | /** 63 | * 解析InputStream输入流,使用Tika简单接口,返回文档内容 64 | * @param stream 要解析的输入流 65 | * @return 解析后的文档内容 66 | * @throws IOException 67 | * @throws TikaException 68 | */ 69 | public static String parseInputStreamWithTika(InputStream stream) throws IOException, TikaException { 70 | return tika.parseToString(stream); 71 | } 72 | 73 | /** 74 | * 检测File文件的MIME类型 75 | * @param file 要检测的文件 76 | * @return MIME类型 77 | * @throws IOException 78 | */ 79 | public static String detectMimeType(File file) throws IOException { 80 | return tika.detect(file); 81 | } 82 | 83 | /** 84 | * 检测InputStream输入流的MIME类型 85 | * @param stream 要检测的输入流 86 | * @return MIME类型 87 | * @throws IOException 88 | */ 89 | public static String detectMimeType(InputStream stream) throws IOException { 90 | return tika.detect(stream); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/hunyuan/chat/entity/HunyuanChatCompletion.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.hunyuan.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import io.github.lnyocly.ai4j.platform.deepseek.chat.entity.DeepSeekChatCompletion; 8 | import io.github.lnyocly.ai4j.platform.hunyuan.HunyuanConstant; 9 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatMessage; 10 | import io.github.lnyocly.ai4j.platform.openai.tool.Tool; 11 | import lombok.AllArgsConstructor; 12 | import lombok.Builder; 13 | import lombok.Data; 14 | import lombok.NoArgsConstructor; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Arrays; 18 | import java.util.List; 19 | 20 | /** 21 | * @Author cly 22 | * @Description 腾讯混元 chat请求实体类 23 | * @Date 2024/8/30 19:26 24 | */ 25 | @Data 26 | @Builder(toBuilder = true) 27 | @NoArgsConstructor() 28 | @AllArgsConstructor() 29 | @JsonIgnoreProperties(ignoreUnknown = true) 30 | @JsonInclude(JsonInclude.Include.NON_NULL) 31 | public class HunyuanChatCompletion { 32 | 33 | private String model; 34 | private List messages; 35 | 36 | @Builder.Default 37 | private Boolean stream = false; 38 | 39 | /** 40 | * 采样温度,介于 0 和 2 之间。更高的值,如 0.8,会使输出更随机,而更低的值,如 0.2,会使其更加集中和确定。 41 | * 我们通常建议可以更改这个值或者更改 top_p,但不建议同时对两者进行修改。 42 | */ 43 | @Builder.Default 44 | private Float temperature = 1f; 45 | 46 | /** 47 | * 作为调节采样温度的替代方案,模型会考虑前 top_p 概率的 token 的结果。所以 0.1 就意味着只有包括在最高 10% 概率中的 token 会被考虑。 48 | * 我们通常建议修改这个值或者更改 temperature,但不建议同时对两者进行修改。 49 | */ 50 | @Builder.Default 51 | @JsonProperty("top_p") 52 | private Float topP = 1f; 53 | 54 | 55 | /** 56 | * 模型可能会调用的 tool 的列表。目前,仅支持 function 作为工具。使用此参数来提供以 JSON 作为输入参数的 function 列表。 57 | */ 58 | private List tools; 59 | 60 | /** 61 | * 辅助属性 62 | */ 63 | @JsonIgnore 64 | private List functions; 65 | 66 | /** 67 | * 控制模型调用 tool 的行为。 68 | * none 意味着模型不会调用任何 tool,而是生成一条消息。 69 | * auto 意味着模型可以选择生成一条消息或调用一个或多个 tool。 70 | * 当没有 tool 时,默认值为 none。如果有 tool 存在,默认值为 auto。 71 | */ 72 | @JsonProperty("tool_choice") 73 | private String toolChoice; 74 | 75 | public static class HunyuanChatCompletionBuilder { 76 | private List functions; 77 | 78 | public HunyuanChatCompletion.HunyuanChatCompletionBuilder functions(String... functions){ 79 | if (this.functions == null) { 80 | this.functions = new ArrayList<>(); 81 | } 82 | this.functions.addAll(Arrays.asList(functions)); 83 | return this; 84 | } 85 | 86 | public HunyuanChatCompletion.HunyuanChatCompletionBuilder functions(List functions){ 87 | if (this.functions == null) { 88 | this.functions = new ArrayList<>(); 89 | } 90 | this.functions.addAll(functions); 91 | return this; 92 | } 93 | 94 | 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/utils/TikTokensUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.utils; 2 | 3 | 4 | import com.knuddels.jtokkit.Encodings; 5 | import com.knuddels.jtokkit.api.Encoding; 6 | import com.knuddels.jtokkit.api.EncodingRegistry; 7 | import com.knuddels.jtokkit.api.EncodingType; 8 | import com.knuddels.jtokkit.api.ModelType; 9 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatMessage; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.commons.lang3.ObjectUtils; 12 | import org.apache.commons.lang3.StringUtils; 13 | 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.Optional; 18 | 19 | /** 20 | * @author cly 21 | */ 22 | @Slf4j 23 | public class TikTokensUtil { 24 | 25 | /** 26 | * 模型名称对应Encoding 27 | */ 28 | private static final Map modelMap = new HashMap<>(); 29 | /** 30 | * registry实例 31 | */ 32 | private static final EncodingRegistry registry = Encodings.newDefaultEncodingRegistry(); 33 | 34 | 35 | static { 36 | for (ModelType model : ModelType.values()){ 37 | Optional encodingForModel = registry.getEncodingForModel(model.getName()); 38 | encodingForModel.ifPresent(encoding -> modelMap.put(model.getName(), encoding)); 39 | } 40 | } 41 | 42 | /** 43 | * 可以简单点用,直接传入list.toString() 44 | * @param encodingType 45 | * @param content 46 | * @return 47 | */ 48 | public static int tokens(EncodingType encodingType, String content){ 49 | Encoding encoding = registry.getEncoding(encodingType); 50 | return encoding.countTokens(content); 51 | } 52 | public static int tokens(String modelName, String content) { 53 | if (StringUtils.isEmpty(content)) { 54 | return 0; 55 | } 56 | Encoding encoding = modelMap.get(modelName); 57 | return encoding.countTokens(content); 58 | } 59 | public static int tokens(String modelName, List messages) { 60 | Encoding encoding = modelMap.get(modelName); 61 | if (ObjectUtils.isEmpty(encoding)) { 62 | throw new IllegalArgumentException("不支持计算Token的模型: " + modelName); 63 | } 64 | 65 | int tokensPerMessage = 0; 66 | int tokensPerName = 0; 67 | if (modelName.startsWith("gpt-4")) { 68 | tokensPerMessage = 3; 69 | tokensPerName = 1; 70 | } else if (modelName.startsWith("gpt-3.5")) { 71 | tokensPerMessage = 4; 72 | tokensPerName = -1; 73 | } 74 | int sum = 0; 75 | for (ChatMessage message : messages) { 76 | 77 | sum += tokensPerMessage; 78 | sum += encoding.countTokens(message.getContent()); 79 | sum += encoding.countTokens(message.getRole()); 80 | if(StringUtils.isNotEmpty(message.getName())){ 81 | sum += encoding.countTokens(message.getName()); 82 | sum += tokensPerName; 83 | } 84 | 85 | 86 | } 87 | sum += 3; // every reply is primed with <|start|>assistant<|message|> 88 | return sum; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/audio/entity/Translation.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.audio.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import io.github.lnyocly.ai4j.platform.openai.audio.enums.WhisperEnum; 7 | import lombok.*; 8 | 9 | import java.io.File; 10 | 11 | /** 12 | * 转录请求参数。 13 | * 可将音频文件转录为你所输入语言对应文本。语言可以自己指定 14 | * 15 | * @author cly 16 | */ 17 | @Data 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | @Builder 21 | @JsonInclude(JsonInclude.Include.NON_NULL) 22 | @JsonIgnoreProperties(ignoreUnknown = true) 23 | public class Translation { 24 | 25 | /** 26 | * 要转录的音频文件对象(不是文件名),采用以下格式之一:flac、mp3、mp4、mpeg、mpga、m4a、ogg、wav 或 webm。 27 | */ 28 | @NonNull 29 | private File file; 30 | 31 | /** 32 | * 要使用的模型的 ID。目前只有 whisper-1 可用。 33 | */ 34 | @NonNull 35 | @Builder.Default 36 | private String model = "whisper-1"; 37 | 38 | /** 39 | * 一个可选文本,用于指导模型的样式或继续上一个音频片段。提示应与音频语言匹配。 40 | */ 41 | private String prompt; 42 | 43 | /** 44 | * 输出的格式,采用以下选项之一:json、text、srt、verbose_json 或 vtt。 45 | */ 46 | @JsonProperty("response_format") 47 | @Builder.Default 48 | private String responseFormat = WhisperEnum.ResponseFormat.JSON.getValue(); 49 | 50 | /** 51 | * 采样温度,介于 0 和 1 之间。较高的值(如 0.8)将使输出更加随机,而较低的值(如 0.2)将使其更具集中性和确定性。如果设置为 0,模型将使用对数概率自动提高温度,直到达到某些阈值。 52 | */ 53 | @Builder.Default 54 | private Double temperature = 0d; 55 | 56 | 57 | public static class TranslationBuilder { 58 | private File file; 59 | 60 | public Translation.TranslationBuilder content(File file){ 61 | // 校验File是否为以下格式之一:flac、mp3、mp4、mpeg、mpga、m4a、ogg、wav 或 webm。 62 | if (file == null) { 63 | throw new IllegalArgumentException("file is required"); 64 | } 65 | 66 | String[] allowedFormats = {"flac", "mp3", "mp4", "mpeg", "mpga", "m4a", "ogg", "wav", "webm"}; 67 | String fileName = file.getName().toLowerCase(); 68 | boolean isValidFormat = false; 69 | 70 | for (String format : allowedFormats) { 71 | if (fileName.endsWith("." + format)) { 72 | isValidFormat = true; 73 | break; 74 | } 75 | } 76 | 77 | if (!isValidFormat) { 78 | throw new IllegalArgumentException("Invalid file format. Allowed formats are: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, webm."); 79 | } 80 | 81 | this.file = file; 82 | return this; 83 | } 84 | } 85 | 86 | public void setFile(@NonNull File file) { 87 | // 校验File是否为以下格式之一:flac、mp3、mp4、mpeg、mpga、m4a、ogg、wav 或 webm。 88 | if (file == null) { 89 | throw new IllegalArgumentException("file is required"); 90 | } 91 | 92 | String[] allowedFormats = {"flac", "mp3", "mp4", "mpeg", "mpga", "m4a", "ogg", "wav", "webm"}; 93 | String fileName = file.getName().toLowerCase(); 94 | boolean isValidFormat = false; 95 | 96 | for (String format : allowedFormats) { 97 | if (fileName.endsWith("." + format)) { 98 | isValidFormat = true; 99 | break; 100 | } 101 | } 102 | 103 | if (!isValidFormat) { 104 | throw new IllegalArgumentException("Invalid file format. Allowed formats are: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, webm."); 105 | } 106 | 107 | this.file = file; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/audio/entity/Transcription.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.audio.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import io.github.lnyocly.ai4j.platform.openai.audio.enums.WhisperEnum; 7 | import lombok.*; 8 | 9 | import java.io.File; 10 | 11 | /** 12 | * 转录请求参数。 13 | * 可将音频文件转录为你所输入语言对应文本。语言可以自己指定 14 | * 15 | * @author cly 16 | */ 17 | @Data 18 | @AllArgsConstructor 19 | @NoArgsConstructor 20 | @Builder 21 | @JsonInclude(JsonInclude.Include.NON_NULL) 22 | @JsonIgnoreProperties(ignoreUnknown = true) 23 | public class Transcription { 24 | 25 | /** 26 | * 要转录的音频文件对象(不是文件名),采用以下格式之一:flac、mp3、mp4、mpeg、mpga、m4a、ogg、wav 或 webm。 27 | */ 28 | @NonNull 29 | private File file; 30 | 31 | /** 32 | * 要使用的模型的 ID。目前只有 whisper-1 可用。 33 | */ 34 | @NonNull 35 | @Builder.Default 36 | private String model = "whisper-1"; 37 | 38 | /** 39 | * 输入音频的语言。以 ISO-639-1 格式提供输入语言将提高准确性和延迟。 40 | */ 41 | private String language; 42 | 43 | /** 44 | * 一个可选文本,用于指导模型的样式或继续上一个音频片段。提示应与音频语言匹配。 45 | */ 46 | private String prompt; 47 | 48 | /** 49 | * 输出的格式,采用以下选项之一:json、text、srt、verbose_json 或 vtt。 50 | */ 51 | @JsonProperty("response_format") 52 | @Builder.Default 53 | private String responseFormat = WhisperEnum.ResponseFormat.JSON.getValue(); 54 | 55 | /** 56 | * 采样温度,介于 0 和 1 之间。较高的值(如 0.8)将使输出更加随机,而较低的值(如 0.2)将使其更具集中性和确定性。如果设置为 0,模型将使用对数概率自动提高温度,直到达到某些阈值。 57 | */ 58 | @Builder.Default 59 | private Double temperature = 0d; 60 | 61 | 62 | public static class TranscriptionBuilder { 63 | private File file; 64 | 65 | public Transcription.TranscriptionBuilder content(File file){ 66 | // 校验File是否为以下格式之一:flac、mp3、mp4、mpeg、mpga、m4a、ogg、wav 或 webm。 67 | if (file == null) { 68 | throw new IllegalArgumentException("file is required"); 69 | } 70 | 71 | String[] allowedFormats = {"flac", "mp3", "mp4", "mpeg", "mpga", "m4a", "ogg", "wav", "webm"}; 72 | String fileName = file.getName().toLowerCase(); 73 | boolean isValidFormat = false; 74 | 75 | for (String format : allowedFormats) { 76 | if (fileName.endsWith("." + format)) { 77 | isValidFormat = true; 78 | break; 79 | } 80 | } 81 | 82 | if (!isValidFormat) { 83 | throw new IllegalArgumentException("Invalid file format. Allowed formats are: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, webm."); 84 | } 85 | 86 | this.file = file; 87 | return this; 88 | } 89 | } 90 | 91 | public void setFile(@NonNull File file) { 92 | // 校验File是否为以下格式之一:flac、mp3、mp4、mpeg、mpga、m4a、ogg、wav 或 webm。 93 | if (file == null) { 94 | throw new IllegalArgumentException("file is required"); 95 | } 96 | 97 | String[] allowedFormats = {"flac", "mp3", "mp4", "mpeg", "mpga", "m4a", "ogg", "wav", "webm"}; 98 | String fileName = file.getName().toLowerCase(); 99 | boolean isValidFormat = false; 100 | 101 | for (String format : allowedFormats) { 102 | if (fileName.endsWith("." + format)) { 103 | isValidFormat = true; 104 | break; 105 | } 106 | } 107 | 108 | if (!isValidFormat) { 109 | throw new IllegalArgumentException("Invalid file format. Allowed formats are: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, webm."); 110 | } 111 | 112 | this.file = file; 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/utils/JsonObjectUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.utils; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import com.alibaba.fastjson2.JSONArray; 5 | import com.alibaba.fastjson2.JSONObject; 6 | 7 | import java.util.Map; 8 | import java.util.Set; 9 | 10 | /** 11 | * @Author cly 12 | * @Description 用于JSON字符串驼峰转换的工具类 13 | * @Date 2024/8/30 23:12 14 | */ 15 | public class JsonObjectUtil { 16 | 17 | // 将 JSON 字符串的字段转换为大写驼峰形式 18 | public static String toCamelCaseWithUppercaseJson(String json) { 19 | return convertJsonString(json, true); 20 | } 21 | 22 | // 将 JSON 字符串的字段转换为下划线形式 23 | public static String toSnakeCaseJson(String json) { 24 | return convertJsonString(json, false); 25 | } 26 | 27 | // 根据参数 isCamelCase 决定转换为驼峰命名还是下划线命名 28 | private static String convertJsonString(String json, boolean isCamelCase) { 29 | JSONObject jsonObject = JSON.parseObject(json); 30 | JSONObject newJsonObject = processJsonObject(jsonObject, isCamelCase); 31 | return newJsonObject.toJSONString(); 32 | } 33 | 34 | private static JSONObject processJsonObject(JSONObject jsonObject, boolean isCamelCase) { 35 | JSONObject newJsonObject = new JSONObject(); 36 | Set> entries = jsonObject.entrySet(); 37 | for (Map.Entry entry : entries) { 38 | String newKey = isCamelCase ? toCamelCaseWithUppercase(entry.getKey()) : toSnakeCase(entry.getKey()); 39 | Object value = entry.getValue(); 40 | 41 | if (value instanceof JSONObject) { 42 | value = processJsonObject((JSONObject) value, isCamelCase); 43 | } else if (value instanceof JSONArray) { 44 | value = processJsonArray((JSONArray) value, isCamelCase); 45 | } 46 | 47 | newJsonObject.put(newKey, value); 48 | } 49 | return newJsonObject; 50 | } 51 | 52 | private static JSONArray processJsonArray(JSONArray jsonArray, boolean isCamelCase) { 53 | JSONArray newArray = new JSONArray(); 54 | for (Object element : jsonArray) { 55 | if (element instanceof JSONObject) { 56 | newArray.add(processJsonObject((JSONObject) element, isCamelCase)); 57 | } else if (element instanceof JSONArray) { 58 | newArray.add(processJsonArray((JSONArray) element, isCamelCase)); 59 | } else { 60 | newArray.add(element); 61 | } 62 | } 63 | return newArray; 64 | } 65 | 66 | private static String toCamelCaseWithUppercase(String key) { 67 | String[] parts = key.split("_"); 68 | StringBuilder camelCaseKey = new StringBuilder(); 69 | for (String part : parts) { 70 | if (part.length() > 0) { 71 | camelCaseKey.append(part.substring(0, 1).toUpperCase()) 72 | .append(part.substring(1).toLowerCase()); 73 | } 74 | } 75 | return camelCaseKey.toString(); 76 | } 77 | 78 | private static String toSnakeCase(String key) { 79 | StringBuilder result = new StringBuilder(); 80 | for (int i = 0; i < key.length(); i++) { 81 | char c = key.charAt(i); 82 | if (Character.isUpperCase(c)) { 83 | if (i > 0) { 84 | result.append("_"); 85 | } 86 | result.append(Character.toLowerCase(c)); 87 | } else { 88 | result.append(c); 89 | } 90 | } 91 | return result.toString(); 92 | } 93 | 94 | public static void main(String[] args) { 95 | JsonObjectUtil util = new JsonObjectUtil(); 96 | 97 | // 测试大写驼峰转换 98 | String camelCaseJson = util.toCamelCaseWithUppercaseJson("{\"request_id\":\"123\",\"created_time\":456}"); 99 | System.out.println(camelCaseJson); 100 | 101 | // 测试下划线转换 102 | String snakeCaseJson = util.toSnakeCaseJson("{\"RequestId\":\"123\",\"CreatedTime\":456}"); 103 | System.out.println(snakeCaseJson); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/moonshot/chat/entity/MoonshotChatCompletion.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.moonshot.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatMessage; 8 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.StreamOptions; 9 | import io.github.lnyocly.ai4j.platform.openai.tool.Tool; 10 | import lombok.*; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | /** 17 | * @Author cly 18 | * @Description 月之暗面对话请求实体 19 | * @Date 2024/8/29 23:13 20 | */ 21 | @Data 22 | @Builder(toBuilder = true) 23 | @NoArgsConstructor() 24 | @AllArgsConstructor() 25 | @JsonIgnoreProperties(ignoreUnknown = true) 26 | @JsonInclude(JsonInclude.Include.NON_NULL) 27 | public class MoonshotChatCompletion { 28 | 29 | 30 | @NonNull 31 | private String model; 32 | 33 | @NonNull 34 | private List messages; 35 | 36 | /** 37 | * 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性。 38 | */ 39 | @Builder.Default 40 | @JsonProperty("frequency_penalty") 41 | private Float frequencyPenalty = 0f; 42 | 43 | /** 44 | * 限制一次请求中模型生成 completion 的最大 token 数。输入 token 和输出 token 的总长度受模型的上下文长度的限制。 45 | */ 46 | @JsonProperty("max_tokens") 47 | private Integer maxTokens; 48 | 49 | /** 50 | * 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其是否已在已有文本中出现受到相应的惩罚,从而增加模型谈论新主题的可能性。 51 | */ 52 | @Builder.Default 53 | @JsonProperty("presence_penalty") 54 | private Float presencePenalty = 0f; 55 | 56 | /** 57 | * 一个 object,指定模型必须输出的格式。 58 | * 59 | * 设置为 { "type": "json_object" } 以启用 JSON 模式,该模式保证模型生成的消息是有效的 JSON。 60 | * 61 | * 注意: 使用 JSON 模式时,你还必须通过系统或用户消息指示模型生成 JSON。 62 | * 否则,模型可能会生成不断的空白字符,直到生成达到令牌限制,从而导致请求长时间运行并显得“卡住”。 63 | * 此外,如果 finish_reason="length",这表示生成超过了 max_tokens 或对话超过了最大上下文长度,消息内容可能会被部分截断。 64 | */ 65 | @JsonProperty("response_format") 66 | private Object responseFormat; 67 | 68 | /** 69 | * 在遇到这些词时,API 将停止生成更多的 token。 70 | */ 71 | private List stop; 72 | 73 | /** 74 | * 如果设置为 True,将会以 SSE(server-sent events)的形式以流式发送消息增量。消息流以 data: [DONE] 结尾 75 | */ 76 | private Boolean stream = false; 77 | 78 | 79 | 80 | /** 81 | * 采样温度,介于 0 和 1 之间。更高的值,如 0.8,会使输出更随机,而更低的值,如 0.2,会使其更加集中和确定。 82 | * 我们通常建议可以更改这个值或者更改 top_p,但不建议同时对两者进行修改。 83 | */ 84 | @Builder.Default 85 | private Float temperature = 0.3f; 86 | 87 | /** 88 | * 作为调节采样温度的替代方案,模型会考虑前 top_p 概率的 token 的结果。所以 0.1 就意味着只有包括在最高 10% 概率中的 token 会被考虑。 89 | * 我们通常建议修改这个值或者更改 temperature,但不建议同时对两者进行修改。 90 | */ 91 | @Builder.Default 92 | @JsonProperty("top_p") 93 | private Float topP = 1f; 94 | 95 | /** 96 | * 模型可能会调用的 tool 的列表。目前,仅支持 function 作为工具。使用此参数来提供以 JSON 作为输入参数的 function 列表。 97 | */ 98 | private List tools; 99 | 100 | /** 101 | * 辅助属性 102 | */ 103 | @JsonIgnore 104 | private List functions; 105 | 106 | /** 107 | * 控制模型调用 tool 的行为。 108 | * none 意味着模型不会调用任何 tool,而是生成一条消息。 109 | * auto 意味着模型可以选择生成一条消息或调用一个或多个 tool。 110 | * 当没有 tool 时,默认值为 none。如果有 tool 存在,默认值为 auto。 111 | */ 112 | @JsonProperty("tool_choice") 113 | private String toolChoice; 114 | 115 | 116 | public static class DeepSeekChatCompletionBuilder { 117 | private List functions; 118 | 119 | public MoonshotChatCompletion.DeepSeekChatCompletionBuilder functions(String... functions){ 120 | if (this.functions == null) { 121 | this.functions = new ArrayList<>(); 122 | } 123 | this.functions.addAll(Arrays.asList(functions)); 124 | return this; 125 | } 126 | 127 | public MoonshotChatCompletion.DeepSeekChatCompletionBuilder functions(List functions){ 128 | if (this.functions == null) { 129 | this.functions = new ArrayList<>(); 130 | } 131 | this.functions.addAll(functions); 132 | return this; 133 | } 134 | 135 | 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/websearch/ChatWithWebSearchEnhance.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.websearch; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import io.github.lnyocly.ai4j.exception.CommonException; 5 | import io.github.lnyocly.ai4j.listener.SseListener; 6 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatCompletion; 7 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatCompletionResponse; 8 | import io.github.lnyocly.ai4j.service.Configuration; 9 | import io.github.lnyocly.ai4j.service.IChatService; 10 | import io.github.lnyocly.ai4j.utils.ValidateUtil; 11 | import io.github.lnyocly.ai4j.websearch.searxng.SearXNGConfig; 12 | import io.github.lnyocly.ai4j.websearch.searxng.SearXNGRequest; 13 | import io.github.lnyocly.ai4j.websearch.searxng.SearXNGResponse; 14 | import okhttp3.OkHttpClient; 15 | import okhttp3.Request; 16 | import okhttp3.Response; 17 | import org.apache.commons.lang3.StringUtils; 18 | 19 | /** 20 | * @Author cly 21 | * @Description TODO 22 | * @Date 2024/12/11 22:32 23 | */ 24 | public class ChatWithWebSearchEnhance implements IChatService { 25 | private final IChatService chatService; 26 | private final SearXNGConfig searXNGConfig; 27 | private final OkHttpClient okHttpClient; 28 | public ChatWithWebSearchEnhance(IChatService chatService, Configuration configuration) { 29 | this.chatService = chatService; 30 | this.searXNGConfig = configuration.getSearXNGConfig(); 31 | this.okHttpClient = configuration.getOkHttpClient(); 32 | } 33 | 34 | @Override 35 | public ChatCompletionResponse chatCompletion(String baseUrl, String apiKey, ChatCompletion chatCompletion) throws Exception { 36 | return chatService.chatCompletion(baseUrl, apiKey, addWebSearchResults(chatCompletion)); 37 | } 38 | 39 | @Override 40 | public ChatCompletionResponse chatCompletion(ChatCompletion chatCompletion) throws Exception { 41 | return chatService.chatCompletion(addWebSearchResults(chatCompletion)); 42 | } 43 | 44 | @Override 45 | public void chatCompletionStream(String baseUrl, String apiKey, ChatCompletion chatCompletion, SseListener eventSourceListener) throws Exception { 46 | chatService.chatCompletionStream(baseUrl, apiKey, addWebSearchResults(chatCompletion), eventSourceListener); 47 | } 48 | 49 | @Override 50 | public void chatCompletionStream(ChatCompletion chatCompletion, SseListener eventSourceListener) throws Exception { 51 | chatService.chatCompletionStream(addWebSearchResults(chatCompletion), eventSourceListener); 52 | } 53 | 54 | 55 | private ChatCompletion addWebSearchResults(ChatCompletion chatCompletion) { 56 | int chatLen = chatCompletion.getMessages().size(); 57 | String prompt = chatCompletion.getMessages().get(chatLen - 1).getContent(); 58 | // 执行联网搜索并将结果附加到提示词中 59 | String searchResults = performWebSearch(prompt); 60 | chatCompletion.getMessages().get(chatLen - 1).setContent("我将提供一段来自互联网的资料信息, 请根据这段资料以及用户提出的问题来给出回答。请确保在回答中使用Markdown格式,并在回答末尾列出参考资料。如果资料中的信息不足以回答用户的问题,可以根据自身知识库进行补充,或者说明无法提供确切的答案。\n" + 61 | "网络资料:\n" 62 | + "============\n" 63 | + searchResults 64 | + "============\n" 65 | + "用户问题:\n" 66 | + "============\n" 67 | + prompt 68 | + "============\n"); 69 | return chatCompletion; 70 | } 71 | 72 | private String performWebSearch(String query) { 73 | 74 | SearXNGRequest searXNGRequest = SearXNGRequest.builder() 75 | .q(query) 76 | .engines(searXNGConfig.getEngines()) 77 | .build(); 78 | 79 | 80 | if(StringUtils.isBlank(searXNGConfig.getUrl())){ 81 | throw new CommonException("SearXNG url is not configured"); 82 | } 83 | 84 | 85 | Request request = new Request.Builder() 86 | .url(ValidateUtil.concatUrl(searXNGConfig.getUrl(), "?format=json&q=" + query + "&engines=" + searXNGConfig.getEngines())) 87 | .get() 88 | .build(); 89 | 90 | 91 | try(Response execute = okHttpClient.newCall(request).execute()) { 92 | if (execute.isSuccessful() && execute.body() != null){ 93 | SearXNGResponse searXNGResponse = JSON.parseObject(execute.body().string(), SearXNGResponse.class); 94 | 95 | if(searXNGResponse.getResults().size() > searXNGConfig.getNums()) { 96 | return JSON.toJSONString(searXNGResponse.getResults().subList(0, searXNGConfig.getNums())); 97 | } 98 | return JSON.toJSONString(searXNGResponse.getResults()); 99 | 100 | 101 | }else{ 102 | throw new CommonException("SearXNG request failed"); 103 | } 104 | 105 | 106 | } catch (Exception e) { 107 | throw new CommonException("SearXNG request failed"); 108 | } 109 | 110 | 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/chat/entity/ChatMessage.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.chat.entity; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import io.github.lnyocly.ai4j.platform.openai.chat.enums.ChatMessageType; 8 | import io.github.lnyocly.ai4j.platform.openai.tool.ToolCall; 9 | import lombok.*; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * @Author cly 16 | * @Description TODO 17 | * @Date 2024/8/3 18:14 18 | */ 19 | @Data 20 | @Builder 21 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 22 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 23 | @JsonIgnoreProperties(ignoreUnknown = true) 24 | @JsonInclude(JsonInclude.Include.NON_NULL) 25 | public class ChatMessage { 26 | private String content; 27 | private String role; 28 | private String name; 29 | private String refusal; 30 | 31 | @JsonProperty("tool_call_id") 32 | private String toolCallId; 33 | 34 | @JsonProperty("tool_calls") 35 | private List toolCalls; 36 | 37 | public ChatMessage(String userMessage) { 38 | this.role = ChatMessageType.USER.getRole(); 39 | this.content = userMessage; 40 | } 41 | public ChatMessage(ChatMessageType role, String message) { 42 | this.role = role.getRole(); 43 | this.content = message; 44 | } 45 | public ChatMessage(String role, String message) { 46 | this.role = role; 47 | this.content = message; 48 | } 49 | 50 | public static ChatMessage withSystem(String content) { 51 | return new ChatMessage(ChatMessageType.SYSTEM, content); 52 | } 53 | 54 | public static ChatMessage withUser(String content) { 55 | return new ChatMessage(ChatMessageType.USER, content); 56 | } 57 | public static ChatMessage withUser(String content, String ...images) { 58 | return ChatMessage.builder() 59 | .role(ChatMessageType.USER.getRole()) 60 | .content(ChatMessage.MultiModal.withMultiModal(content, images)) 61 | .build(); 62 | } 63 | 64 | public static ChatMessage withAssistant(String content) { 65 | return new ChatMessage(ChatMessageType.ASSISTANT, content); 66 | } 67 | public static ChatMessage withAssistant(List toolCalls) { 68 | return ChatMessage.builder() 69 | .role(ChatMessageType.ASSISTANT.getRole()) 70 | .toolCalls(toolCalls) 71 | .build(); 72 | } 73 | 74 | public static ChatMessage withTool(String content, String toolCallId) { 75 | return ChatMessage.builder() 76 | .role(ChatMessageType.TOOL.getRole()) 77 | .content(content) 78 | .toolCallId(toolCallId) 79 | .build(); 80 | } 81 | 82 | 83 | public static class ChatMessageBuilder { 84 | private String content; 85 | 86 | /** 87 | * 多模态消息内容 88 | * 89 | * @param content 多模态消息内容 90 | * @return 91 | */ 92 | public ChatMessageBuilder content(List content){ 93 | this.content = JSON.toJSONString(content); 94 | return this; 95 | } 96 | public ChatMessageBuilder content(String content){ 97 | this.content = content; 98 | return this; 99 | } 100 | 101 | 102 | } 103 | 104 | @Data 105 | @Builder 106 | @NoArgsConstructor 107 | @AllArgsConstructor 108 | @JsonIgnoreProperties(ignoreUnknown = true) 109 | @JsonInclude(JsonInclude.Include.NON_NULL) 110 | public static class MultiModal { 111 | private String type = Type.TEXT.type; 112 | private String text; 113 | @JsonProperty("image_url") 114 | private ImageUrl imageUrl; 115 | 116 | 117 | @Data 118 | @NoArgsConstructor 119 | @AllArgsConstructor 120 | public static class ImageUrl { 121 | private String url; 122 | } 123 | 124 | @Getter 125 | @AllArgsConstructor 126 | public enum Type { 127 | TEXT("text", "文本类型"), 128 | IMAGE_URL("image_url", "图片类型,可以为url或者base64"), 129 | ; 130 | private final String type; 131 | private final String info; 132 | } 133 | 134 | public static List withMultiModal(String text, String... imageUrl) { 135 | List messages = new ArrayList<>(); 136 | messages.add(new MultiModal(Type.TEXT.getType(), text, null)); 137 | for (String url : imageUrl) { 138 | messages.add(new MultiModal(Type.IMAGE_URL.getType(), null, new ImageUrl(url))); 139 | } 140 | return messages; 141 | } 142 | 143 | } 144 | 145 | 146 | 147 | 148 | 149 | } 150 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/service/factor/AiService.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.service.factor; 2 | 3 | import io.github.lnyocly.ai4j.platform.baichuan.chat.BaichuanChatService; 4 | import io.github.lnyocly.ai4j.platform.deepseek.chat.DeepSeekChatService; 5 | import io.github.lnyocly.ai4j.platform.hunyuan.chat.HunyuanChatService; 6 | import io.github.lnyocly.ai4j.platform.lingyi.chat.LingyiChatService; 7 | import io.github.lnyocly.ai4j.platform.minimax.chat.MinimaxChatService; 8 | import io.github.lnyocly.ai4j.platform.moonshot.chat.MoonshotChatService; 9 | import io.github.lnyocly.ai4j.platform.ollama.chat.OllamaAiChatService; 10 | import io.github.lnyocly.ai4j.platform.openai.audio.OpenAiAudioService; 11 | import io.github.lnyocly.ai4j.platform.openai.chat.OpenAiChatService; 12 | import io.github.lnyocly.ai4j.platform.openai.realtime.OpenAiRealtimeService; 13 | import io.github.lnyocly.ai4j.platform.zhipu.chat.ZhipuChatService; 14 | import io.github.lnyocly.ai4j.service.*; 15 | import io.github.lnyocly.ai4j.platform.openai.embedding.OpenAiEmbeddingService; 16 | import io.github.lnyocly.ai4j.vector.service.PineconeService; 17 | import io.github.lnyocly.ai4j.websearch.ChatWithWebSearchEnhance; 18 | 19 | /** 20 | * @Author cly 21 | * @Description AI服务工厂,创建各种AI应用 22 | * @Date 2024/8/7 18:10 23 | */ 24 | public class AiService { 25 | // private final ConcurrentMap chatServiceCache = new ConcurrentHashMap<>(); 26 | //private final ConcurrentMap embeddingServiceCache = new ConcurrentHashMap<>(); 27 | 28 | private final Configuration configuration; 29 | 30 | public AiService(Configuration configuration) { 31 | this.configuration = configuration; 32 | } 33 | 34 | public IChatService getChatService(PlatformType platform) { 35 | //return chatServiceCache.computeIfAbsent(platform, this::createChatService); 36 | return createChatService(platform); 37 | } 38 | 39 | public IChatService webSearchEnhance(IChatService chatService) { 40 | //IChatService chatService = getChatService(platform); 41 | return new ChatWithWebSearchEnhance(chatService, configuration); 42 | } 43 | 44 | private IChatService createChatService(PlatformType platform) { 45 | switch (platform) { 46 | case OPENAI: 47 | return new OpenAiChatService(configuration); 48 | case ZHIPU: 49 | return new ZhipuChatService(configuration); 50 | case DEEPSEEK: 51 | return new DeepSeekChatService(configuration); 52 | case MOONSHOT: 53 | return new MoonshotChatService(configuration); 54 | case HUNYUAN: 55 | return new HunyuanChatService(configuration); 56 | case LINGYI: 57 | return new LingyiChatService(configuration); 58 | case OLLAMA: 59 | return new OllamaAiChatService(configuration); 60 | case MINIMAX: 61 | return new MinimaxChatService(configuration); 62 | case BAICHUAN: 63 | return new BaichuanChatService(configuration); 64 | default: 65 | throw new IllegalArgumentException("Unknown platform: " + platform); 66 | } 67 | } 68 | 69 | 70 | 71 | public IEmbeddingService getEmbeddingService(PlatformType platform) { 72 | //return embeddingServiceCache.computeIfAbsent(platform, this::createEmbeddingService); 73 | return createEmbeddingService(platform); 74 | } 75 | 76 | private IEmbeddingService createEmbeddingService(PlatformType platform) { 77 | switch (platform) { 78 | case OPENAI: 79 | return new OpenAiEmbeddingService(configuration); 80 | default: 81 | throw new IllegalArgumentException("Unknown platform: " + platform); 82 | } 83 | } 84 | 85 | public IAudioService getAudioService(PlatformType platform) { 86 | return createAudioService(platform); 87 | } 88 | 89 | private IAudioService createAudioService(PlatformType platform) { 90 | switch (platform) { 91 | case OPENAI: 92 | return new OpenAiAudioService(configuration); 93 | default: 94 | throw new IllegalArgumentException("Unknown platform: " + platform); 95 | } 96 | } 97 | 98 | public IRealtimeService getRealtimeService(PlatformType platform) { 99 | return createRealtimeService(platform); 100 | } 101 | 102 | private IRealtimeService createRealtimeService(PlatformType platform) { 103 | switch (platform) { 104 | case OPENAI: 105 | return new OpenAiRealtimeService(configuration); 106 | default: 107 | throw new IllegalArgumentException("Unknown platform: " + platform); 108 | } 109 | } 110 | 111 | public PineconeService getPineconeService() { 112 | return new PineconeService(configuration); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/deepseek/chat/entity/DeepSeekChatCompletion.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.deepseek.chat.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatMessage; 8 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.StreamOptions; 9 | import io.github.lnyocly.ai4j.platform.openai.tool.Tool; 10 | import io.github.lnyocly.ai4j.platform.zhipu.chat.entity.ZhipuChatCompletion; 11 | import lombok.*; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | 17 | /** 18 | * @Author cly 19 | * @Description DeepSeek对话请求实体 20 | * @Date 2024/8/29 10:27 21 | */ 22 | @Data 23 | @Builder(toBuilder = true) 24 | @NoArgsConstructor() 25 | @AllArgsConstructor() 26 | @JsonIgnoreProperties(ignoreUnknown = true) 27 | @JsonInclude(JsonInclude.Include.NON_NULL) 28 | public class DeepSeekChatCompletion { 29 | 30 | 31 | @NonNull 32 | private String model; 33 | 34 | @NonNull 35 | private List messages; 36 | 37 | /** 38 | * 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性。 39 | */ 40 | @Builder.Default 41 | @JsonProperty("frequency_penalty") 42 | private Float frequencyPenalty = 0f; 43 | 44 | /** 45 | * 限制一次请求中模型生成 completion 的最大 token 数。输入 token 和输出 token 的总长度受模型的上下文长度的限制。 46 | */ 47 | @JsonProperty("max_tokens") 48 | private Integer maxTokens; 49 | 50 | /** 51 | * 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其是否已在已有文本中出现受到相应的惩罚,从而增加模型谈论新主题的可能性。 52 | */ 53 | @Builder.Default 54 | @JsonProperty("presence_penalty") 55 | private Float presencePenalty = 0f; 56 | 57 | /** 58 | * 一个 object,指定模型必须输出的格式。 59 | * 60 | * 设置为 { "type": "json_object" } 以启用 JSON 模式,该模式保证模型生成的消息是有效的 JSON。 61 | * 62 | * 注意: 使用 JSON 模式时,你还必须通过系统或用户消息指示模型生成 JSON。 63 | * 否则,模型可能会生成不断的空白字符,直到生成达到令牌限制,从而导致请求长时间运行并显得“卡住”。 64 | * 此外,如果 finish_reason="length",这表示生成超过了 max_tokens 或对话超过了最大上下文长度,消息内容可能会被部分截断。 65 | */ 66 | @JsonProperty("response_format") 67 | private Object responseFormat; 68 | 69 | /** 70 | * 在遇到这些词时,API 将停止生成更多的 token。 71 | */ 72 | private List stop; 73 | 74 | /** 75 | * 如果设置为 True,将会以 SSE(server-sent events)的形式以流式发送消息增量。消息流以 data: [DONE] 结尾 76 | */ 77 | private Boolean stream = false; 78 | 79 | /** 80 | * 流式输出相关选项。只有在 stream 参数为 true 时,才可设置此参数。 81 | */ 82 | @Builder.Default 83 | @JsonProperty("stream_options") 84 | private StreamOptions streamOptions = new StreamOptions(); 85 | 86 | /** 87 | * 采样温度,介于 0 和 2 之间。更高的值,如 0.8,会使输出更随机,而更低的值,如 0.2,会使其更加集中和确定。 88 | * 我们通常建议可以更改这个值或者更改 top_p,但不建议同时对两者进行修改。 89 | */ 90 | @Builder.Default 91 | private Float temperature = 1f; 92 | 93 | /** 94 | * 作为调节采样温度的替代方案,模型会考虑前 top_p 概率的 token 的结果。所以 0.1 就意味着只有包括在最高 10% 概率中的 token 会被考虑。 95 | * 我们通常建议修改这个值或者更改 temperature,但不建议同时对两者进行修改。 96 | */ 97 | @Builder.Default 98 | @JsonProperty("top_p") 99 | private Float topP = 1f; 100 | 101 | /** 102 | * 模型可能会调用的 tool 的列表。目前,仅支持 function 作为工具。使用此参数来提供以 JSON 作为输入参数的 function 列表。 103 | */ 104 | private List tools; 105 | 106 | /** 107 | * 辅助属性 108 | */ 109 | @JsonIgnore 110 | private List functions; 111 | 112 | /** 113 | * 控制模型调用 tool 的行为。 114 | * none 意味着模型不会调用任何 tool,而是生成一条消息。 115 | * auto 意味着模型可以选择生成一条消息或调用一个或多个 tool。 116 | * 当没有 tool 时,默认值为 none。如果有 tool 存在,默认值为 auto。 117 | */ 118 | @JsonProperty("tool_choice") 119 | private String toolChoice; 120 | 121 | /** 122 | * 是否返回所输出 token 的对数概率。如果为 true,则在 message 的 content 中返回每个输出 token 的对数概率。 123 | */ 124 | @Builder.Default 125 | private Boolean logprobs = false; 126 | 127 | /** 128 | * 一个介于 0 到 20 之间的整数 N,指定每个输出位置返回输出概率 top N 的 token,且返回这些 token 的对数概率。指定此参数时,logprobs 必须为 true。 129 | */ 130 | @JsonProperty("top_logprobs") 131 | private Integer topLogprobs; 132 | 133 | public static class DeepSeekChatCompletionBuilder { 134 | private List functions; 135 | 136 | public DeepSeekChatCompletion.DeepSeekChatCompletionBuilder functions(String... functions){ 137 | if (this.functions == null) { 138 | this.functions = new ArrayList<>(); 139 | } 140 | this.functions.addAll(Arrays.asList(functions)); 141 | return this; 142 | } 143 | 144 | public DeepSeekChatCompletion.DeepSeekChatCompletionBuilder functions(List functions){ 145 | if (this.functions == null) { 146 | this.functions = new ArrayList<>(); 147 | } 148 | this.functions.addAll(functions); 149 | return this; 150 | } 151 | 152 | 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/utils/RecursiveCharacterTextSplitter.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.utils; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | /** 9 | * @Author cly 10 | * @Description 分词器 11 | * @Date 2024/8/2 22:59 12 | */ 13 | @Slf4j 14 | public class RecursiveCharacterTextSplitter { 15 | private List separators; 16 | private int chunkSize = 500; 17 | private int chunkOverlap = 50; 18 | 19 | // 构造函数,接受分隔符列表、块大小和块重叠作为参数 20 | public RecursiveCharacterTextSplitter(List separators, int chunkSize, int chunkOverlap) { 21 | // 如果分隔符列表为null,则使用默认值 22 | if (separators == null) { 23 | this.separators = Arrays.asList("\n\n", "\n", " ", ""); 24 | } else { 25 | this.separators = separators; 26 | } 27 | this.chunkSize = chunkSize; 28 | this.chunkOverlap = chunkOverlap; 29 | } 30 | 31 | public RecursiveCharacterTextSplitter(int chunkSize, int chunkOverlap) { 32 | this.separators = Arrays.asList("\n\n", "\n", " ", ""); 33 | this.chunkSize = chunkSize; 34 | this.chunkOverlap = chunkOverlap; 35 | } 36 | 37 | // 将文本分割成块的方法 38 | public List splitText(String text) { 39 | // 声明一个空的字符串列表,用于存储最终的文本块 40 | List finalChunks = new ArrayList<>(); 41 | String separator = separators.get(separators.size() - 1); 42 | 43 | // 循环遍历分隔符列表,找到可以在文本中找到的最合适的分隔符 44 | for (String s : separators) { 45 | if (text.contains(s) || s.isEmpty()) { 46 | separator = s; 47 | break; 48 | } 49 | } 50 | 51 | List splits = Arrays.asList(text.split(separator)); 52 | 53 | // 声明一个空的字符串列表,用于存储长度小于块大小的子字符串 54 | List goodSplits = new ArrayList<>(); 55 | // 循环遍历子字符串列表,将较短的子字符串添加到goodSplits列表中,将较长的子字符串递归地传递给splitText方法 56 | for (String s : splits) { 57 | if (s.length() < chunkSize) { 58 | goodSplits.add(s); 59 | } else { 60 | if (!goodSplits.isEmpty()) { 61 | // 将goodSplits列表中的子字符串合并为一个文本块,并将其添加到最终的文本块列表中 62 | List mergedText = mergeSplits(goodSplits, separator); 63 | finalChunks.addAll(mergedText); 64 | goodSplits.clear(); 65 | } 66 | // 递归地将较长的子字符串传递给splitText方法 67 | List otherInfo = splitText(s); 68 | finalChunks.addAll(otherInfo); 69 | } 70 | } 71 | 72 | if (!goodSplits.isEmpty()) { 73 | List mergedText = mergeSplits(goodSplits, separator); 74 | finalChunks.addAll(mergedText); 75 | } 76 | 77 | return finalChunks; 78 | } 79 | 80 | private List mergeSplits(List splits, String separator) { 81 | int separatorLen = separator.length(); 82 | 83 | List docs = new ArrayList<>(); 84 | List currentDoc = new ArrayList<>(); 85 | int total = 0; 86 | 87 | for (String d : splits) { 88 | int len = d.length(); 89 | if (total + len + (separatorLen > 0 && !currentDoc.isEmpty() ? separatorLen : 0) > chunkSize) { 90 | if (total > chunkSize) { 91 | log.warn("Warning: Created a chunk of size {}, which is longer than the specified {}", total, chunkSize); 92 | } 93 | if (!currentDoc.isEmpty()) { 94 | String doc = joinDocs(currentDoc, separator); 95 | if (doc != null) { 96 | docs.add(doc); 97 | } 98 | // 通过移除currentDoc中的文档,将currentDoc的长度减小到指定的文档重叠长度chunkOverlap或更小, 结果存到下一个chunk的开始位置 99 | while (total > chunkOverlap || (total + len + (separatorLen > 0 && !currentDoc.isEmpty() ? separatorLen : 0) > chunkSize && total > 0)) { 100 | total -= currentDoc.get(0).length() + (separatorLen > 0 && currentDoc.size() > 1 ? separatorLen : 0); 101 | currentDoc.remove(0); 102 | } 103 | } 104 | } 105 | currentDoc.add(d); 106 | total += len + (separatorLen > 0 && currentDoc.size() > 1 ? separatorLen : 0); 107 | } 108 | 109 | String doc = joinDocs(currentDoc, separator); 110 | if (doc != null) { 111 | docs.add(doc); 112 | } 113 | 114 | return docs; 115 | } 116 | 117 | private String joinDocs(List docs, String separator) { 118 | if (docs.isEmpty()) { 119 | return null; 120 | } 121 | StringBuilder sb = new StringBuilder(); 122 | for (int i = 0; i < docs.size(); i++) { 123 | sb.append(docs.get(i)); 124 | if (i < docs.size() - 1) { 125 | sb.append(separator); 126 | } 127 | } 128 | return sb.toString(); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/chat/entity/ChatCompletion.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.chat.entity; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 6 | import com.fasterxml.jackson.annotation.JsonInclude; 7 | import com.fasterxml.jackson.annotation.JsonProperty; 8 | import io.github.lnyocly.ai4j.platform.openai.tool.Tool; 9 | import lombok.*; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | /** 17 | * @Author cly 18 | * @Description ChatCompletion 实体类 19 | * @Date 2024/8/3 18:00 20 | */ 21 | @Data 22 | @Builder(toBuilder = true) 23 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 24 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 25 | @JsonIgnoreProperties(ignoreUnknown = true) 26 | @JsonInclude(JsonInclude.Include.NON_NULL) 27 | public class ChatCompletion { 28 | 29 | /** 30 | * 对话模型 31 | */ 32 | @NonNull 33 | private String model; 34 | 35 | /** 36 | * 消息内容 37 | */ 38 | @NonNull 39 | @Singular 40 | private List messages; 41 | 42 | /** 43 | * 如果设置为 True,将会以 SSE(server-sent events)的形式以流式发送消息增量。消息流以 data: [DONE] 结尾 44 | */ 45 | @Builder.Default 46 | private Boolean stream = false; 47 | 48 | /** 49 | * 流式输出相关选项。只有在 stream 参数为 true 时,才可设置此参数。 50 | */ 51 | @JsonProperty("stream_options") 52 | private StreamOptions streamOptions; 53 | 54 | /** 55 | * 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性。 56 | */ 57 | @Builder.Default 58 | @JsonProperty("frequency_penalty") 59 | private Float frequencyPenalty = 0f; 60 | 61 | /** 62 | * 采样温度,介于 0 和 2 之间。更高的值,如 0.8,会使输出更随机,而更低的值,如 0.2,会使其更加集中和确定。 63 | * 我们通常建议可以更改这个值或者更改 top_p,但不建议同时对两者进行修改。 64 | */ 65 | @Builder.Default 66 | private Float temperature = 1f; 67 | 68 | /** 69 | * 作为调节采样温度的替代方案,模型会考虑前 top_p 概率的 token 的结果。所以 0.1 就意味着只有包括在最高 10% 概率中的 token 会被考虑。 70 | * 我们通常建议修改这个值或者更改 temperature,但不建议同时对两者进行修改。 71 | */ 72 | @Builder.Default 73 | @JsonProperty("top_p") 74 | private Float topP = 1f; 75 | 76 | /** 77 | * 限制一次请求中模型生成 completion 的最大 token 数。输入 token 和输出 token 的总长度受模型的上下文长度的限制。 78 | */ 79 | @JsonProperty("max_tokens") 80 | private Integer maxTokens; 81 | 82 | /** 83 | * 模型可能会调用的 tool 的列表。目前,仅支持 function 作为工具。使用此参数来提供以 JSON 作为输入参数的 function 列表。 84 | */ 85 | private List tools; 86 | 87 | /** 88 | * 辅助属性 89 | */ 90 | @JsonIgnore 91 | private List functions; 92 | 93 | /** 94 | * 控制模型调用 tool 的行为。 95 | * none 意味着模型不会调用任何 tool,而是生成一条消息。 96 | * auto 意味着模型可以选择生成一条消息或调用一个或多个 tool。 97 | * 当没有 tool 时,默认值为 none。如果有 tool 存在,默认值为 auto。 98 | */ 99 | @JsonProperty("tool_choice") 100 | private String toolChoice; 101 | 102 | @Builder.Default 103 | @JsonProperty("parallel_tool_calls") 104 | private Boolean parallelToolCalls = true; 105 | 106 | /** 107 | * 一个 object,指定模型必须输出的格式。 108 | * 109 | * 设置为 { "type": "json_object" } 以启用 JSON 模式,该模式保证模型生成的消息是有效的 JSON。 110 | * 111 | * 注意: 使用 JSON 模式时,你还必须通过系统或用户消息指示模型生成 JSON。 112 | * 否则,模型可能会生成不断的空白字符,直到生成达到令牌限制,从而导致请求长时间运行并显得“卡住”。 113 | * 此外,如果 finish_reason="length",这表示生成超过了 max_tokens 或对话超过了最大上下文长度,消息内容可能会被部分截断。 114 | */ 115 | @JsonProperty("response_format") 116 | private Object responseFormat; 117 | 118 | private String user; 119 | 120 | @Builder.Default 121 | private Integer n = 1; 122 | 123 | /** 124 | * 在遇到这些词时,API 将停止生成更多的 token。 125 | */ 126 | private List stop; 127 | 128 | /** 129 | * 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其是否已在已有文本中出现受到相应的惩罚,从而增加模型谈论新主题的可能性。 130 | */ 131 | @Builder.Default 132 | @JsonProperty("presence_penalty") 133 | private Float presencePenalty = 0f; 134 | 135 | @JsonProperty("logit_bias") 136 | private Map logitBias; 137 | 138 | /** 139 | * 是否返回所输出 token 的对数概率。如果为 true,则在 message 的 content 中返回每个输出 token 的对数概率。 140 | */ 141 | @Builder.Default 142 | private Boolean logprobs = false; 143 | 144 | /** 145 | * 一个介于 0 到 20 之间的整数 N,指定每个输出位置返回输出概率 top N 的 token,且返回这些 token 的对数概率。指定此参数时,logprobs 必须为 true。 146 | */ 147 | @JsonProperty("top_logprobs") 148 | private Integer topLogprobs; 149 | 150 | 151 | public static class ChatCompletionBuilder { 152 | private List functions; 153 | 154 | public ChatCompletion.ChatCompletionBuilder functions(String... functions){ 155 | if (this.functions == null) { 156 | this.functions = new ArrayList<>(); 157 | } 158 | this.functions.addAll(Arrays.asList(functions)); 159 | return this; 160 | } 161 | 162 | public ChatCompletion.ChatCompletionBuilder functions(List functions){ 163 | if (this.functions == null) { 164 | this.functions = new ArrayList<>(); 165 | } 166 | this.functions.addAll(functions); 167 | return this; 168 | } 169 | 170 | 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/utils/BearerTokenUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.utils; 2 | 3 | import com.auth0.jwt.JWT; 4 | import com.auth0.jwt.algorithms.Algorithm; 5 | import com.google.common.cache.Cache; 6 | import com.google.common.cache.CacheBuilder; 7 | 8 | import javax.crypto.Mac; 9 | import javax.crypto.spec.SecretKeySpec; 10 | import javax.xml.bind.DatatypeConverter; 11 | import java.nio.charset.StandardCharsets; 12 | import java.security.MessageDigest; 13 | import java.text.SimpleDateFormat; 14 | import java.util.Date; 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | import java.util.TimeZone; 18 | import java.util.concurrent.TimeUnit; 19 | 20 | 21 | public class BearerTokenUtils { 22 | // 过期时间;默认24小时 23 | private static final long EXPIRE_MILLIS = 24 * 60 * 60 * 1000L; 24 | 25 | // 缓存服务 26 | public static Cache cache = CacheBuilder.newBuilder() 27 | .initialCapacity(100) 28 | .expireAfterWrite(EXPIRE_MILLIS - (60 * 1000L), TimeUnit.MILLISECONDS) 29 | .build(); 30 | 31 | /** 32 | * 对 API Key 进行签名 33 | * 新版机制中平台颁发的 API Key 同时包含 “用户标识 id” 和 “签名密钥 secret”,即格式为 {id}.{secret} 34 | * 35 | * @param apiKey 智谱APIkey 36 | * @return Token 37 | */ 38 | public static String getToken(String apiKey) { 39 | // 分割APIKEY 40 | String[] args = apiKey.split("\\."); 41 | if (args.length != 2) { 42 | throw new IllegalArgumentException("API Key 格式错误"); 43 | } 44 | String id = args[0]; 45 | String secret = args[1]; 46 | // 缓存Token 47 | String token = cache.getIfPresent(apiKey); 48 | if (null != token) return token; 49 | // 创建Token 50 | Algorithm algorithm = Algorithm.HMAC256(secret.getBytes(StandardCharsets.UTF_8)); 51 | Map payload = new HashMap<>(); 52 | payload.put("api_key", id); 53 | payload.put("exp", System.currentTimeMillis() + EXPIRE_MILLIS); 54 | payload.put("timestamp", System.currentTimeMillis()); 55 | Map headerClaims = new HashMap<>(); 56 | headerClaims.put("alg", "HS256"); 57 | headerClaims.put("sign_type", "SIGN"); 58 | token = JWT.create().withPayload(payload).withHeader(headerClaims).sign(algorithm); 59 | cache.put(id, token); 60 | return token; 61 | } 62 | 63 | /** 64 | * apiKey 属于SecretId与SecretKey的拼接,格式为 {SecretId}.{SecretKey} 65 | * 66 | * @param apiKey 腾讯混元APIkey 67 | * @return 68 | */ 69 | public static String getAuthorization(String apiKey, String action, String payloadJson) throws Exception { 70 | String[] args = apiKey.split("\\."); 71 | if (args.length != 2) { 72 | throw new IllegalArgumentException("API Key 格式错误"); 73 | } 74 | String id = args[0]; 75 | String key = args[1]; 76 | 77 | String algorithm = "TC3-HMAC-SHA256"; 78 | String service = "hunyuan"; 79 | String host = "hunyuan.tencentcloudapi.com"; 80 | String timestamp = String.valueOf(System.currentTimeMillis() / 1000); 81 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 82 | // 注意时区,否则容易出错 83 | sdf.setTimeZone(TimeZone.getTimeZone("UTC")); 84 | String date = sdf.format(new Date(Long.valueOf(timestamp + "000"))); 85 | 86 | // ************* 步骤 1:拼接规范请求串 ************* 87 | String httpRequestMethod = "POST"; 88 | String canonicalUri = "/"; 89 | String canonicalQueryString = ""; 90 | String canonicalHeaders = "content-type:application/json; charset=utf-8\n" + "host:" + host + "\n" + "x-tc-action:" + action.toLowerCase() + "\n"; 91 | String signedHeaders = "content-type;host;x-tc-action"; 92 | String hashedRequestPayload = sha256Hex(payloadJson); 93 | 94 | String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n" 95 | + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload; 96 | 97 | // ************* 步骤 2:拼接待签名字符串 ************* 98 | String credentialScope = date + "/" + service + "/" + "tc3_request"; 99 | String hashedCanonicalRequest = sha256Hex(canonicalRequest); 100 | 101 | String stringToSign = algorithm + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest; 102 | 103 | // ************* 步骤 3:计算签名 ************* 104 | byte[] secretDate = hmac256(("TC3" + key).getBytes(StandardCharsets.UTF_8), date); 105 | byte[] secretService = hmac256(secretDate, service); 106 | byte[] secretSigning = hmac256(secretService, "tc3_request"); 107 | 108 | String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase(); 109 | 110 | // ************* 步骤 4:拼接 Authorization ************* 111 | String authorization = algorithm + " " + "Credential=" + id + "/" + credentialScope + ", " 112 | + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature; 113 | return authorization; 114 | } 115 | 116 | private static byte[] hmac256(byte[] key, String msg) throws Exception { 117 | Mac mac = Mac.getInstance("HmacSHA256"); 118 | SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm()); 119 | mac.init(secretKeySpec); 120 | return mac.doFinal(msg.getBytes(StandardCharsets.UTF_8)); 121 | } 122 | 123 | private static String sha256Hex(String s) throws Exception { 124 | MessageDigest md = MessageDigest.getInstance("SHA-256"); 125 | byte[] d = md.digest(s.getBytes(StandardCharsets.UTF_8)); 126 | return DatatypeConverter.printHexBinary(d).toLowerCase(); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/realtime/RealtimeConstant.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.realtime; 2 | 3 | /** 4 | * @Author cly 5 | * @Description TODO 6 | * @Date 2024/10/13 13:56 7 | */ 8 | public class RealtimeConstant { 9 | /** 10 | * session.update 11 | * input_audio_buffer.append 12 | * input_audio_buffer.commit 13 | * input_audio_buffer.clear 14 | * conversation.item.create 15 | * conversation.item.truncate 16 | * conversation.item.delete 17 | * response.create 18 | * response.cancel 19 | */ 20 | public static class ClientEvent { 21 | public static final String SESSION_UPDATE = "session.update"; 22 | public static final String INPUT_AUDIO_BUFFER_APPEND = "input_audio_buffer.append"; 23 | public static final String INPUT_AUDIO_BUFFER_COMMIT = "input_audio_buffer.commit"; 24 | public static final String INPUT_AUDIO_BUFFER_CLEAR = "input_audio_buffer.clear"; 25 | public static final String CONVERSATION_ITEM_CREATE = "conversation.item.create"; 26 | public static final String CONVERSATION_ITEM_TRUNCATE = "conversation.item.truncate"; 27 | public static final String CONVERSATION_ITEM_DELETE = "conversation.item.delete"; 28 | /** 29 | * 发送此事件可触发响应生成。 30 | */ 31 | public static final String RESPONSE_CREATE = "response.create"; 32 | public static final String RESPONSE_CANCEL = "response.cancel"; 33 | } 34 | 35 | /** 36 | * error 37 | * session.created 38 | * session.updated 39 | * conversation.created 40 | * input_audio_buffer.committed 41 | * input_audio_buffer.cleared 42 | * input_audio_buffer.speech_started 43 | * input_audio_buffer.speech_stopped 44 | * conversation.item.created 45 | * conversation.item.input_audio_transcription.completed 46 | * conversation.item.input_audio_transcription.failed 47 | * conversation.item.truncated 48 | * conversation.item.deleted 49 | * response.created 50 | * response.done 51 | * response.output_item.added 52 | * response.output_item.done 53 | * response.content_part.added 54 | * response.content_part.done 55 | * response.text.delta 56 | * response.text.done 57 | * response.audio_transcript.delta 58 | * response.audio_transcript.done 59 | * response.audio.delta 60 | * response.audio.done 61 | * response.function_call_arguments.delta 62 | * response.function_call_arguments.done 63 | * rate_limits.updated 64 | */ 65 | public static class ServerEvent { 66 | public static final String ERROR = "error"; 67 | public static final String SESSION_CREATED = "session.created"; 68 | public static final String SESSION_UPDATED = "session.updated"; 69 | /** 70 | * 创建对话时返回。会话创建后立即发出。 71 | */ 72 | public static final String CONVERSATION_CREATED = "conversation.created"; 73 | public static final String INPUT_AUDIO_BUFFER_COMMITTED = "input_audio_buffer.committed"; 74 | public static final String INPUT_AUDIO_BUFFER_CLEARED = "input_audio_buffer.cleared"; 75 | public static final String INPUT_AUDIO_BUFFER_SPEECH_STARTED = "input_audio_buffer.speech_started"; 76 | public static final String INPUT_AUDIO_BUFFER_SPEECH_STOPPED = "input_audio_buffer.speech_stopped"; 77 | /** 78 | * 创建对话项目时返回。 79 | */ 80 | public static final String CONVERSATION_ITEM_CREATED = "conversation.item.created"; 81 | public static final String CONVERSATION_ITEM_INPUT_AUDIO_TRANSCRIPTION_COMPLETED = "conversation.item.input_audio_transcription.completed"; 82 | public static final String CONVERSATION_ITEM_INPUT_AUDIO_TRANSCRIPTION_FAILED = "conversation.item.input_audio_transcription.failed"; 83 | public static final String CONVERSATION_ITEM_TRUNCATED = "conversation.item.truncated"; 84 | public static final String CONVERSATION_ITEM_DELETED = "conversation.item.deleted"; 85 | public static final String RESPONSE_CREATED = "response.created"; 86 | /** 87 | * 当响应完成流式传输时返回。无论最终状态如何,始终发出。 88 | */ 89 | public static final String RESPONSE_DONE = "response.done"; 90 | public static final String RESPONSE_OUTPUT_ITEM_ADDED = "response.output_item.added"; 91 | public static final String RESPONSE_OUTPUT_ITEM_DONE = "response.output_item.done"; 92 | /** 93 | * 在生成回复过程中将新内容添加到助理信息项目时返回。 94 | */ 95 | public static final String RESPONSE_CONTENT_PART_ADDED = "response.content_part.added"; 96 | /** 97 | * 当助手信息项目中的内容部分完成流式传输时返回。当响应中断、不完整或取消时也会返回。 98 | */ 99 | public static final String RESPONSE_CONTENT_PART_DONE = "response.content_part.done"; 100 | /** 101 | * 当“文本”内容部分的文本值更新时返回。 102 | */ 103 | public static final String RESPONSE_TEXT_DELTA = "response.text.delta"; 104 | /** 105 | * 当 “文本 ”内容部分的文本值完成流式传输时返回。当响应被中断、不完整或取消时也会返回。 106 | */ 107 | public static final String RESPONSE_TEXT_DONE = "response.text.done"; 108 | public static final String RESPONSE_AUDIO_TRANSCRIPT_DELTA = "response.audio_transcript.delta"; 109 | public static final String RESPONSE_AUDIO_TRANSCRIPT_DONE = "response.audio_transcript.done"; 110 | /** 111 | * 当模型生成的音频更新时返回。 112 | */ 113 | public static final String RESPONSE_AUDIO_DELTA = "response.audio.delta"; 114 | /** 115 | * 当模型生成的音频完成时返回。当响应被中断、不完整或取消时也会发出。 116 | */ 117 | public static final String RESPONSE_AUDIO_DONE = "response.audio.done"; 118 | public static final String RESPONSE_FUNCTION_CALL_ARGUMENTS_DELTA = "response.function_call_arguments.delta"; 119 | public static final String RESPONSE_FUNCTION_CALL_ARGUMENTS_DONE = "response.function_call_arguments.done"; 120 | public static final String RATE_LIMITS_UPDATED = "rate_limits.updated"; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ai4j/src/test/java/io/github/lnyocly/BaichuanTest.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly; 2 | 3 | import io.github.lnyocly.ai4j.config.BaichuanConfig; 4 | import io.github.lnyocly.ai4j.interceptor.ErrorInterceptor; 5 | import io.github.lnyocly.ai4j.listener.SseListener; 6 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatCompletion; 7 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatCompletionResponse; 8 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatMessage; 9 | import io.github.lnyocly.ai4j.service.Configuration; 10 | import io.github.lnyocly.ai4j.service.IChatService; 11 | import io.github.lnyocly.ai4j.service.PlatformType; 12 | import io.github.lnyocly.ai4j.service.factor.AiService; 13 | import io.github.lnyocly.ai4j.utils.OkHttpUtil; 14 | import lombok.extern.slf4j.Slf4j; 15 | import okhttp3.OkHttpClient; 16 | import okhttp3.logging.HttpLoggingInterceptor; 17 | import org.junit.Before; 18 | import org.junit.Test; 19 | 20 | import java.security.KeyManagementException; 21 | import java.security.NoSuchAlgorithmException; 22 | import java.util.concurrent.TimeUnit; 23 | 24 | /** 25 | * @Author cly 26 | * @Description 智谱测试类 27 | * @Date 2024/8/3 18:22 28 | */ 29 | @Slf4j 30 | public class BaichuanTest { 31 | 32 | private IChatService chatService; 33 | 34 | @Before 35 | public void test_init() throws NoSuchAlgorithmException, KeyManagementException { 36 | BaichuanConfig baichuanConfig = new BaichuanConfig(); 37 | baichuanConfig.setApiKey("sk-4e5717ac51cacaf5d590cff13630cfce"); 38 | 39 | Configuration configuration = new Configuration(); 40 | configuration.setBaichuanConfig(baichuanConfig); 41 | 42 | 43 | HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(); 44 | httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS); 45 | 46 | OkHttpClient okHttpClient = new OkHttpClient 47 | .Builder() 48 | .addInterceptor(httpLoggingInterceptor) 49 | .addInterceptor(new ErrorInterceptor()) 50 | .connectTimeout(300, TimeUnit.SECONDS) 51 | .writeTimeout(300, TimeUnit.SECONDS) 52 | .readTimeout(300, TimeUnit.SECONDS) 53 | .sslSocketFactory(OkHttpUtil.getIgnoreInitedSslContext().getSocketFactory(), OkHttpUtil.IGNORE_SSL_TRUST_MANAGER_X509) 54 | .hostnameVerifier(OkHttpUtil.getIgnoreSslHostnameVerifier()) 55 | //.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10809))) 56 | .build(); 57 | configuration.setOkHttpClient(okHttpClient); 58 | 59 | AiService aiService = new AiService(configuration); 60 | 61 | chatService = aiService.getChatService(PlatformType.BAICHUAN); 62 | } 63 | 64 | 65 | @Test 66 | public void test_chatCompletions_common() throws Exception { 67 | ChatCompletion chatCompletion = ChatCompletion.builder() 68 | .model("Baichuan4") 69 | .message(ChatMessage.withUser("鲁迅为什么打周树人")) 70 | .build(); 71 | 72 | System.out.println("请求参数"); 73 | System.out.println(chatCompletion); 74 | 75 | ChatCompletionResponse chatCompletionResponse = chatService.chatCompletion(chatCompletion); 76 | 77 | System.out.println("请求成功"); 78 | System.out.println(chatCompletionResponse); 79 | 80 | } 81 | 82 | @Test 83 | public void test_chatCompletions_multimodal() throws Exception { 84 | ChatCompletion chatCompletion = ChatCompletion.builder() 85 | .model("yi-vision") 86 | .message(ChatMessage.withUser("这几张图片,分别有什么动物, 并且是什么品种", 87 | "https://tse2-mm.cn.bing.net/th/id/OIP-C.SVxZtXIcz3LbcE4ZeS6jEgHaE7?w=231&h=180&c=7&r=0&o=5&dpr=1.3&pid=1.7", 88 | "https://ts3.cn.mm.bing.net/th?id=OIP-C.BYyILFgs3ATnTEQ-B5ApFQHaFj&w=288&h=216&c=8&rs=1&qlt=90&o=6&dpr=1.3&pid=3.1&rm=2")) 89 | .build(); 90 | 91 | System.out.println("请求参数"); 92 | System.out.println(chatCompletion); 93 | 94 | ChatCompletionResponse chatCompletionResponse = chatService.chatCompletion(chatCompletion); 95 | 96 | System.out.println("请求成功"); 97 | System.out.println(chatCompletionResponse); 98 | } 99 | 100 | 101 | @Test 102 | public void test_chatCompletions_stream() throws Exception { 103 | ChatCompletion chatCompletion = ChatCompletion.builder() 104 | .model("gpt-4o-mini") 105 | .message(ChatMessage.withUser("鲁迅为什么打周树人")) 106 | .build(); 107 | 108 | 109 | System.out.println("请求参数"); 110 | System.out.println(chatCompletion); 111 | 112 | // 构造监听器 113 | SseListener sseListener = new SseListener() { 114 | @Override 115 | protected void send() { 116 | System.out.println(this.getCurrStr()); 117 | } 118 | }; 119 | 120 | chatService.chatCompletionStream(chatCompletion, sseListener); 121 | 122 | System.out.println("请求成功"); 123 | System.out.println(sseListener.getOutput()); 124 | System.out.println(sseListener.getUsage()); 125 | 126 | } 127 | 128 | @Test 129 | public void test_chatCompletions_function() throws Exception { 130 | ChatCompletion chatCompletion = ChatCompletion.builder() 131 | .model("gpt-4o-mini") 132 | .message(ChatMessage.withUser("查询洛阳明天的天气,并告诉我火车是否发车")) 133 | .functions("queryWeather", "queryTrainInfo") 134 | .build(); 135 | 136 | System.out.println("请求参数"); 137 | System.out.println(chatCompletion); 138 | 139 | ChatCompletionResponse chatCompletionResponse = chatService.chatCompletion(chatCompletion); 140 | 141 | System.out.println("请求成功"); 142 | System.out.println(chatCompletionResponse); 143 | 144 | System.out.println(chatCompletion); 145 | 146 | } 147 | 148 | @Test 149 | public void test_chatCompletions_stream_function() throws Exception { 150 | 151 | // 构造请求参数 152 | ChatCompletion chatCompletion = ChatCompletion.builder() 153 | .model("yi-large-fc") 154 | .message(ChatMessage.withUser("查询洛阳明天的天气")) 155 | .functions("queryWeather", "queryTrainInfo") 156 | .build(); 157 | 158 | 159 | // 构造监听器 160 | SseListener sseListener = new SseListener() { 161 | @Override 162 | protected void send() { 163 | System.out.println(this.getCurrStr()); 164 | } 165 | }; 166 | // 显示函数参数,默认不显示 167 | sseListener.setShowToolArgs(true); 168 | 169 | // 发送SSE请求 170 | chatService.chatCompletionStream(chatCompletion, sseListener); 171 | System.out.println("完整内容: "); 172 | System.out.println(sseListener.getOutput()); 173 | System.out.println("内容花费: "); 174 | System.out.println(sseListener.getUsage()); 175 | } 176 | 177 | 178 | } 179 | -------------------------------------------------------------------------------- /ai4j/src/test/java/io/github/lnyocly/MinimaxTest.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly; 2 | 3 | import io.github.lnyocly.ai4j.config.MinimaxConfig; 4 | import io.github.lnyocly.ai4j.interceptor.ErrorInterceptor; 5 | import io.github.lnyocly.ai4j.listener.SseListener; 6 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatCompletion; 7 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatCompletionResponse; 8 | import io.github.lnyocly.ai4j.platform.openai.chat.entity.ChatMessage; 9 | import io.github.lnyocly.ai4j.service.Configuration; 10 | import io.github.lnyocly.ai4j.service.IChatService; 11 | import io.github.lnyocly.ai4j.service.PlatformType; 12 | import io.github.lnyocly.ai4j.service.factor.AiService; 13 | import io.github.lnyocly.ai4j.utils.OkHttpUtil; 14 | import lombok.extern.slf4j.Slf4j; 15 | import okhttp3.OkHttpClient; 16 | import okhttp3.logging.HttpLoggingInterceptor; 17 | import org.junit.Before; 18 | import org.junit.Test; 19 | 20 | import java.security.KeyManagementException; 21 | import java.security.NoSuchAlgorithmException; 22 | import java.util.concurrent.TimeUnit; 23 | 24 | /** 25 | * @Author : isxuwl 26 | * @Date: 2024/10/15 16:08 27 | * @Model Description: minimax 测试类 28 | * @Description: 29 | */ 30 | @Slf4j 31 | public class MinimaxTest { 32 | 33 | private IChatService chatService; 34 | 35 | @Before 36 | public void test_init() throws NoSuchAlgorithmException, KeyManagementException { 37 | MinimaxConfig minimaxConfig = new MinimaxConfig(); 38 | // minimaxConfig.setApiKey("sk-123456789"); 39 | 40 | Configuration configuration = new Configuration(); 41 | configuration.setMinimaxConfig(minimaxConfig); 42 | 43 | 44 | HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(); 45 | httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS); 46 | 47 | OkHttpClient okHttpClient = new OkHttpClient 48 | .Builder() 49 | .addInterceptor(httpLoggingInterceptor) 50 | .addInterceptor(new ErrorInterceptor()) 51 | .connectTimeout(300, TimeUnit.SECONDS) 52 | .writeTimeout(300, TimeUnit.SECONDS) 53 | .readTimeout(300, TimeUnit.SECONDS) 54 | .sslSocketFactory(OkHttpUtil.getIgnoreInitedSslContext().getSocketFactory(), OkHttpUtil.IGNORE_SSL_TRUST_MANAGER_X509) 55 | .hostnameVerifier(OkHttpUtil.getIgnoreSslHostnameVerifier()) 56 | //.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 10809))) 57 | .build(); 58 | configuration.setOkHttpClient(okHttpClient); 59 | 60 | AiService aiService = new AiService(configuration); 61 | 62 | chatService = aiService.getChatService(PlatformType.MINIMAX); 63 | 64 | } 65 | 66 | 67 | @Test 68 | public void test_chatCompletions_common() throws Exception { 69 | ChatCompletion chatCompletion = ChatCompletion.builder() 70 | .model("abab6.5s-chat") 71 | .message(ChatMessage.withUser("鲁迅为什么打周树人")) 72 | .build(); 73 | 74 | System.out.println("请求参数"); 75 | System.out.println(chatCompletion); 76 | 77 | ChatCompletionResponse chatCompletionResponse = chatService.chatCompletion(chatCompletion); 78 | 79 | System.out.println("请求成功"); 80 | System.out.println(chatCompletionResponse); 81 | 82 | } 83 | 84 | @Test 85 | public void test_chatCompletions_multimodal() throws Exception { 86 | ChatCompletion chatCompletion = ChatCompletion.builder() 87 | .model("yi-vision") 88 | .message(ChatMessage.withUser("这几张图片,分别有什么动物, 并且是什么品种", 89 | "https://tse2-mm.cn.bing.net/th/id/OIP-C.SVxZtXIcz3LbcE4ZeS6jEgHaE7?w=231&h=180&c=7&r=0&o=5&dpr=1.3&pid=1.7", 90 | "https://ts3.cn.mm.bing.net/th?id=OIP-C.BYyILFgs3ATnTEQ-B5ApFQHaFj&w=288&h=216&c=8&rs=1&qlt=90&o=6&dpr=1.3&pid=3.1&rm=2")) 91 | .build(); 92 | 93 | System.out.println("请求参数"); 94 | System.out.println(chatCompletion); 95 | 96 | ChatCompletionResponse chatCompletionResponse = chatService.chatCompletion(chatCompletion); 97 | 98 | System.out.println("请求成功"); 99 | System.out.println(chatCompletionResponse); 100 | } 101 | 102 | 103 | @Test 104 | public void test_chatCompletions_stream() throws Exception { 105 | ChatCompletion chatCompletion = ChatCompletion.builder() 106 | .model("abab6.5s-chat") 107 | .message(ChatMessage.withUser("鲁迅为什么打周树人")) 108 | .build(); 109 | 110 | 111 | System.out.println("请求参数"); 112 | System.out.println(chatCompletion); 113 | 114 | // 构造监听器 115 | SseListener sseListener = new SseListener() { 116 | @Override 117 | protected void send() { 118 | System.out.println(this.getCurrStr()); 119 | } 120 | }; 121 | 122 | chatService.chatCompletionStream(chatCompletion, sseListener); 123 | 124 | System.out.println("请求成功"); 125 | System.out.println(sseListener.getOutput()); 126 | System.out.println(sseListener.getUsage()); 127 | 128 | } 129 | 130 | @Test 131 | public void test_chatCompletions_function() throws Exception { 132 | ChatCompletion chatCompletion = ChatCompletion.builder() 133 | .model("gpt-4o-mini") 134 | .message(ChatMessage.withUser("查询洛阳明天的天气,并告诉我火车是否发车")) 135 | .functions("queryWeather", "queryTrainInfo") 136 | .build(); 137 | 138 | System.out.println("请求参数"); 139 | System.out.println(chatCompletion); 140 | 141 | ChatCompletionResponse chatCompletionResponse = chatService.chatCompletion(chatCompletion); 142 | 143 | System.out.println("请求成功"); 144 | System.out.println(chatCompletionResponse); 145 | 146 | System.out.println(chatCompletion); 147 | 148 | } 149 | 150 | @Test 151 | public void test_chatCompletions_stream_function() throws Exception { 152 | 153 | // 构造请求参数 154 | ChatCompletion chatCompletion = ChatCompletion.builder() 155 | .model("yi-large-fc") 156 | .message(ChatMessage.withUser("查询洛阳明天的天气")) 157 | .functions("queryWeather", "queryTrainInfo") 158 | .build(); 159 | 160 | 161 | // 构造监听器 162 | SseListener sseListener = new SseListener() { 163 | @Override 164 | protected void send() { 165 | System.out.println(this.getCurrStr()); 166 | } 167 | }; 168 | // 显示函数参数,默认不显示 169 | sseListener.setShowToolArgs(true); 170 | 171 | // 发送SSE请求 172 | chatService.chatCompletionStream(chatCompletion, sseListener); 173 | System.out.println("完整内容: "); 174 | System.out.println(sseListener.getOutput()); 175 | System.out.println("内容花费: "); 176 | System.out.println(sseListener.getUsage()); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/vector/service/PineconeService.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.vector.service; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import io.github.lnyocly.ai4j.config.PineconeConfig; 5 | import io.github.lnyocly.ai4j.constant.Constants; 6 | import io.github.lnyocly.ai4j.exception.CommonException; 7 | import io.github.lnyocly.ai4j.service.Configuration; 8 | import io.github.lnyocly.ai4j.utils.ValidateUtil; 9 | import io.github.lnyocly.ai4j.vector.VertorDataEntity; 10 | import io.github.lnyocly.ai4j.vector.pinecone.*; 11 | import lombok.extern.slf4j.Slf4j; 12 | import okhttp3.*; 13 | 14 | import java.io.IOException; 15 | import java.util.ArrayList; 16 | import java.util.HashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.stream.Collectors; 20 | 21 | /** 22 | * @Author cly 23 | * @Description TODO 24 | * @Date 2024/8/16 17:09 25 | */ 26 | @Slf4j 27 | public class PineconeService { 28 | private final PineconeConfig pineconeConfig; 29 | private final OkHttpClient okHttpClient; 30 | 31 | 32 | public PineconeService(Configuration configuration) { 33 | this.pineconeConfig = configuration.getPineconeConfig(); 34 | this.okHttpClient = configuration.getOkHttpClient(); 35 | } 36 | 37 | // 插入Pinecone向量库 38 | public Integer insert(PineconeInsert pineconeInsertReq){ 39 | Request request = new Request.Builder() 40 | .url(ValidateUtil.concatUrl(pineconeConfig.getHost(), pineconeConfig.getUpsert())) 41 | .post(RequestBody.create(MediaType.parse(Constants.APPLICATION_JSON), JSON.toJSONString(pineconeInsertReq))) 42 | .header("accept", Constants.APPLICATION_JSON) 43 | .header("content-type", Constants.APPLICATION_JSON) 44 | .header("Api-Key", pineconeConfig.getKey()) 45 | .build(); 46 | 47 | try (Response response = okHttpClient.newCall(request).execute()) { 48 | if (!response.isSuccessful()) { 49 | log.error("Error inserting into Pinecone vector store: {}", response.message()); 50 | throw new CommonException("Error inserting into Pinecone: " + response.message()); 51 | } 52 | 53 | // {"upsertedCount":3} 54 | return JSON.parseObject(response.body().string(), PineconeInsertResponse.class).getUpsertedCount(); 55 | } catch (Exception e) { 56 | log.error("OkHttpClient exception! {}", e.getMessage(), e); 57 | throw new CommonException("Failed to insert into Pinecone due to network error." + e.getMessage()); 58 | } 59 | } 60 | 61 | public Integer insert(VertorDataEntity vertorDataEntity, String namespace) { 62 | int count = vertorDataEntity.getContent().size(); 63 | List pineconeVectors = new ArrayList<>(); 64 | // 生成每个向量的id 65 | List ids = generateIDs(count); 66 | // 生成每个向量对应的文本,元数据,kv 67 | List> metadatas = generateContent(vertorDataEntity.getContent()); 68 | 69 | for(int i = 0;i < count; ++i){ 70 | pineconeVectors.add(new PineconeVectors(ids.get(i), vertorDataEntity.getVector().get(i), metadatas.get(i))); 71 | } 72 | PineconeInsert pineconeInsert = new PineconeInsert(pineconeVectors, namespace); 73 | return this.insert(pineconeInsert); 74 | } 75 | 76 | // 从Pinecone向量库中查询相似向量 77 | public PineconeQueryResponse query(PineconeQuery pineconeQueryReq){ 78 | Request request = new Request.Builder() 79 | .url(ValidateUtil.concatUrl(pineconeConfig.getHost(), pineconeConfig.getQuery())) 80 | .post(RequestBody.create(MediaType.parse(Constants.APPLICATION_JSON), JSON.toJSONString(pineconeQueryReq))) 81 | .header("accept", Constants.APPLICATION_JSON) 82 | .header("content-type", Constants.APPLICATION_JSON) 83 | .header("Api-Key", pineconeConfig.getKey()) 84 | .build(); 85 | 86 | try (Response response = okHttpClient.newCall(request).execute()) { 87 | if (!response.isSuccessful()) { 88 | log.error("Error querying Pinecone vector store: {}", response.message()); 89 | throw new CommonException("Error querying Pinecone: " + response.message()); 90 | } 91 | 92 | String body = response.body().string(); 93 | return JSON.parseObject(body, PineconeQueryResponse.class); 94 | } catch (IOException e) { 95 | log.error("OkHttpClient exception! {}", e.getMessage(), e); 96 | throw new CommonException("Failed to query Pinecone due to network error." + e.getMessage()); 97 | } 98 | } 99 | 100 | public String query(PineconeQuery pineconeQuery, String delimiter){ 101 | PineconeQueryResponse queryResponse = this.query(pineconeQuery); 102 | if(delimiter == null) delimiter = ""; 103 | return queryResponse.getMatches().stream().map(match -> match.getMetadata().get(Constants.METADATA_KEY)).collect(Collectors.joining(delimiter)); 104 | } 105 | 106 | // 从Pinecone向量库中删除向量 107 | public Boolean delete(PineconeDelete pineconeDeleteReq){ 108 | Request request = new Request.Builder() 109 | .url(ValidateUtil.concatUrl(pineconeConfig.getHost(), pineconeConfig.getDelete())) 110 | .post(RequestBody.create(MediaType.parse(Constants.APPLICATION_JSON), JSON.toJSONString(pineconeDeleteReq))) 111 | .header("accept", Constants.APPLICATION_JSON) 112 | .header("content-type", Constants.APPLICATION_JSON) 113 | .header("Api-Key", pineconeConfig.getKey()) 114 | .build(); 115 | 116 | try (Response response = okHttpClient.newCall(request).execute()) { 117 | if (!response.isSuccessful()) { 118 | log.error("Error deleting from Pinecone vector store: {}", response.message()); 119 | throw new CommonException("Error deleting from Pinecone: " + response.message()); 120 | } 121 | return true; 122 | } catch (IOException e) { 123 | log.error("OkHttpClient exception! {}", e.getMessage(), e); 124 | throw new CommonException("Failed to delete from Pinecone due to network error." + e.getMessage()); 125 | } 126 | } 127 | 128 | // 生成每个向量的id 129 | public List generateIDs(int count){ 130 | List ids = new ArrayList<>(); 131 | for (long i = 0L; i < count; ++i) { 132 | ids.add("id_" + i); 133 | } 134 | return ids; 135 | } 136 | 137 | 138 | // 生成每个向量对应的文本 139 | public List> generateContent(List contents){ 140 | List> finalcontents = new ArrayList<>(); 141 | 142 | for(int i = 0; i < contents.size(); i++){ 143 | HashMap map = new HashMap<>(); 144 | map.put(Constants.METADATA_KEY, contents.get(i)); 145 | finalcontents.add(map); 146 | } 147 | return finalcontents; 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /ai4j/src/main/java/io/github/lnyocly/ai4j/platform/openai/audio/OpenAiAudioService.java: -------------------------------------------------------------------------------- 1 | package io.github.lnyocly.ai4j.platform.openai.audio; 2 | 3 | import com.alibaba.fastjson2.JSON; 4 | import io.github.lnyocly.ai4j.config.OpenAiConfig; 5 | import io.github.lnyocly.ai4j.constant.Constants; 6 | import io.github.lnyocly.ai4j.platform.openai.audio.entity.*; 7 | import io.github.lnyocly.ai4j.service.Configuration; 8 | import io.github.lnyocly.ai4j.service.IAudioService; 9 | import io.github.lnyocly.ai4j.utils.ValidateUtil; 10 | import okhttp3.*; 11 | import org.apache.commons.lang3.StringUtils; 12 | 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | 16 | /** 17 | * @Author cly 18 | * @Description OpenAi音频服务 19 | * @Date 2024/10/10 23:36 20 | */ 21 | public class OpenAiAudioService implements IAudioService { 22 | private final OpenAiConfig openAiConfig; 23 | private final OkHttpClient okHttpClient; 24 | 25 | public OpenAiAudioService(Configuration configuration) { 26 | this.openAiConfig = configuration.getOpenAiConfig(); 27 | this.okHttpClient = configuration.getOkHttpClient(); 28 | } 29 | 30 | 31 | @Override 32 | public InputStream textToSpeech(String baseUrl, String apiKey, TextToSpeech textToSpeech) { 33 | if(baseUrl == null || "".equals(baseUrl)) baseUrl = openAiConfig.getApiHost(); 34 | if(apiKey == null || "".equals(apiKey)) apiKey = openAiConfig.getApiKey(); 35 | 36 | String requestString = JSON.toJSONString(textToSpeech); 37 | 38 | Request request = new Request.Builder() 39 | .header("Authorization", "Bearer " + apiKey) 40 | .url(ValidateUtil.concatUrl(baseUrl, openAiConfig.getSpeechUrl())) 41 | .post(RequestBody.create(MediaType.parse("application/json"), requestString)) 42 | .build(); 43 | 44 | // 发送请求并获取响应 45 | try (Response response = okHttpClient.newCall(request).execute()) { 46 | // 检查响应是否成功 47 | if (!response.isSuccessful()) { 48 | throw new IOException("Unexpected code " + response); 49 | } 50 | 51 | // 获取响应体 52 | ResponseBody responseBody = response.body(); 53 | if (responseBody != null) { 54 | return responseBody.byteStream(); 55 | } 56 | 57 | } catch (IOException e) { 58 | e.printStackTrace(); 59 | } 60 | return null; 61 | } 62 | 63 | @Override 64 | public InputStream textToSpeech(TextToSpeech textToSpeech) { 65 | return this.textToSpeech(null, null, textToSpeech); 66 | } 67 | 68 | @Override 69 | public TranscriptionResponse transcription(String baseUrl, String apiKey, Transcription transcription) { 70 | if(baseUrl == null || "".equals(baseUrl)) baseUrl = openAiConfig.getApiHost(); 71 | if(apiKey == null || "".equals(apiKey)) apiKey = openAiConfig.getApiKey(); 72 | 73 | 74 | // 创建请求体 75 | MultipartBody.Builder builder = new MultipartBody.Builder() 76 | .setType(MultipartBody.FORM) 77 | .addFormDataPart("file", transcription.getFile().getName(), 78 | RequestBody.create(MediaType.parse("application/octet-stream"), transcription.getFile())) 79 | .addFormDataPart("model", transcription.getModel()) 80 | .addFormDataPart("temperature", String.valueOf(transcription.getTemperature())); 81 | if(StringUtils.isNotBlank(transcription.getLanguage())){ 82 | builder.addFormDataPart("language", transcription.getLanguage()); 83 | } 84 | if(StringUtils.isNotBlank(transcription.getPrompt())){ 85 | builder.addFormDataPart("prompt", transcription.getPrompt()); 86 | } 87 | if(StringUtils.isNotBlank(transcription.getResponseFormat())){ 88 | builder.addFormDataPart("response_format", transcription.getResponseFormat()); 89 | } 90 | 91 | MultipartBody multipartBody = builder.build(); 92 | 93 | 94 | // 创建请求 95 | Request request = new Request.Builder() 96 | .header("Authorization", "Bearer " + apiKey) 97 | .url(ValidateUtil.concatUrl(baseUrl, openAiConfig.getTranscriptionUrl())) 98 | .post(multipartBody) 99 | .build(); 100 | 101 | // 发送请求并获取响应 102 | try (Response response = okHttpClient.newCall(request).execute()) { 103 | if (response.isSuccessful()) { 104 | String res = response.body().string(); 105 | TranscriptionResponse transcriptionResponse = JSON.parseObject(res, TranscriptionResponse.class); 106 | return transcriptionResponse; 107 | } 108 | } catch (Exception e) { 109 | e.printStackTrace(); 110 | } 111 | 112 | return null; 113 | } 114 | 115 | @Override 116 | public TranscriptionResponse transcription(Transcription transcription) { 117 | return this.transcription(null, null, transcription); 118 | } 119 | 120 | @Override 121 | public TranslationResponse translation(String baseUrl, String apiKey, Translation translation) { 122 | if(baseUrl == null || "".equals(baseUrl)) baseUrl = openAiConfig.getApiHost(); 123 | if(apiKey == null || "".equals(apiKey)) apiKey = openAiConfig.getApiKey(); 124 | 125 | // 创建请求体 126 | MultipartBody.Builder builder = new MultipartBody.Builder() 127 | .setType(MultipartBody.FORM) 128 | .addFormDataPart("file", translation.getFile().getName(), 129 | RequestBody.create(MediaType.parse("application/octet-stream"), translation.getFile())) 130 | .addFormDataPart("model", translation.getModel()) 131 | .addFormDataPart("temperature", String.valueOf(translation.getTemperature())); 132 | if(StringUtils.isNotBlank(translation.getPrompt())){ 133 | builder.addFormDataPart("prompt", translation.getPrompt()); 134 | } 135 | if(StringUtils.isNotBlank(translation.getResponseFormat())){ 136 | builder.addFormDataPart("response_format", translation.getResponseFormat()); 137 | } 138 | 139 | MultipartBody multipartBody = builder.build(); 140 | 141 | // 创建请求 142 | Request request = new Request.Builder() 143 | .header("Authorization", "Bearer " + apiKey) 144 | .url(ValidateUtil.concatUrl(baseUrl, openAiConfig.getTranslationUrl())) 145 | .post(multipartBody) 146 | .build(); 147 | 148 | // 发送请求并获取响应 149 | try (Response response = okHttpClient.newCall(request).execute()) { 150 | if (response.isSuccessful()) { 151 | String res = response.body().string(); 152 | TranslationResponse translationResponse = JSON.parseObject(res, TranslationResponse.class); 153 | return translationResponse; 154 | } 155 | } catch (Exception e) { 156 | e.printStackTrace(); 157 | } 158 | 159 | return null; 160 | } 161 | 162 | @Override 163 | public TranslationResponse translation(Translation translation) { 164 | return this.translation(null, null, translation); 165 | } 166 | } 167 | --------------------------------------------------------------------------------