新特性:新增hutool-ai模块

This commit is contained in:
choweli 2025-03-19 15:31:36 +08:00
parent bb4491b48e
commit e118864530
45 changed files with 3297 additions and 0 deletions

17
hutool-ai/README.md Normal file
View File

@ -0,0 +1,17 @@
<p align="center">
<a href="https://hutool.cn/"><img src="https://plus.hutool.cn/images/hutool.svg" width="45%"></a>
</p>
<p align="center">
<strong>🍬Make Java Sweet Again.</strong>
</p>
<p align="center">
👉 <a href="https://hutool.cn">https://hutool.cn/</a> 👈
</p>
## 📚Hutool-ai 模块介绍
`Hutool-ai`提供了AI大模型的封装。
-------------------------------------------------------------------------------
## 🛠️包含内容

54
hutool-ai/pom.xml Normal file
View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent>
<groupId>org.dromara.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>6.0.0-M21</version>
</parent>
<artifactId>hutool-ai</artifactId>
<name>${project.artifactId}</name>
<description>Hutool AI大模型封装</description>
<properties>
<Automatic-Module-Name>org.dromara.hutool.ai</Automatic-Module-Name>
</properties>
<dependencies>
<dependency>
<groupId>org.dromara.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.dromara.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.dromara.hutool</groupId>
<artifactId>hutool-log</artifactId>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.dromara.hutool</groupId>
<artifactId>hutool-json</artifactId>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.dromara.hutool</groupId>
<artifactId>hutool-swing</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,35 @@
package org.dromara.hutool.ai;
import org.dromara.hutool.core.exception.ExceptionUtil;
import org.dromara.hutool.core.text.StrUtil;
/**
* 异常处理类
*/
public class AIException extends RuntimeException {
private static final long serialVersionUID = 1L;
public AIException(Throwable e) {
super(ExceptionUtil.getMessage(e), e);
}
public AIException(String message) {
super(message);
}
public AIException(String messageTemplate, Object... params) {
super(StrUtil.format(messageTemplate, params));
}
public AIException(String message, Throwable throwable) {
super(message, throwable);
}
public AIException(String message, Throwable throwable, boolean enableSuppression, boolean writableStackTrace) {
super(message, throwable, enableSuppression, writableStackTrace);
}
public AIException(Throwable throwable, String messageTemplate, Object... params) {
super(StrUtil.format(messageTemplate, params), throwable);
}
}

View File

@ -0,0 +1,61 @@
package org.dromara.hutool.ai;
import org.dromara.hutool.ai.core.AIConfig;
import org.dromara.hutool.ai.core.AIService;
import org.dromara.hutool.ai.core.AIServiceProvider;
import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap;
import java.util.Map;
import java.util.ServiceLoader;
/**
* 创建AIModelService的工厂类
*
* @author elichow
* @since 6.0.0
*/
public class AIServiceFactory {
private static final Map<String, AIServiceProvider> providers = new SafeConcurrentHashMap<>();
// 加载所有 AIModelProvider 实现类
static {
ServiceLoader<AIServiceProvider> loader = ServiceLoader.load(AIServiceProvider.class);
for (AIServiceProvider provider : loader) {
providers.put(provider.getServiceName().toLowerCase(), provider);
}
}
/**
* 获取AI服务
*
* @param config AIConfig配置
* @return AI服务实例
* @since 6.0.0
*/
public static AIService getAIService(AIConfig config) {
return getAIService(config, AIService.class);
}
/**
* 获取AI服务
*
* @param config AIConfig配置
* @param clazz AI服务类
* @return clazz对应的AI服务类实例
* @since 6.0.0
*/
public static <T extends AIService> T getAIService(AIConfig config, Class<T> clazz) {
AIServiceProvider provider = providers.get(config.getModelName().toLowerCase());
if (provider == null) {
throw new IllegalArgumentException("Unsupported model: " + config.getModelName());
}
AIService service = provider.create(config);
if (!clazz.isInstance(service)) {
throw new AIException("Model service is not of type: " + clazz.getSimpleName());
}
return (T) service;
}
}

View File

@ -0,0 +1,112 @@
package org.dromara.hutool.ai;
import org.dromara.hutool.ai.core.AIConfig;
import org.dromara.hutool.ai.core.AIService;
import org.dromara.hutool.ai.core.Message;
import org.dromara.hutool.ai.model.deepseek.DeepSeekService;
import org.dromara.hutool.ai.model.doubao.DoubaoService;
import org.dromara.hutool.ai.model.grok.GrokService;
import org.dromara.hutool.ai.model.openai.OpenaiService;
import java.util.List;
/**
* AI工具类
*
* @author elichow
* @since 6.0.0
*/
public class AIUtil {
/**
* 获取AI模型服务每个大模型提供的功能会不一样可以调用此方法指定不同AI服务类调用不同的功能
*
* @param config 创建的AI服务模型的配置
* @param clazz AI模型服务类
* @return AIModelService的实现类实例
* @since 6.0.0
*/
public static <T extends AIService> T getAIService(AIConfig config, Class<T> clazz) {
return AIServiceFactory.getAIService(config, clazz);
}
/**
* 获取AI模型服务
*
* @param config 创建的AI服务模型的配置
* @return AIModelService 其中只有公共方法
* @since 6.0.0
*/
public static AIService getAIService(AIConfig config) {
return getAIService(config, AIService.class);
}
/**
* 获取DeepSeek模型服务
*
* @param config 创建的AI服务模型的配置
* @return DeepSeekService
* @since 6.0.0
*/
public static DeepSeekService getDeepSeekService(AIConfig config) {
return getAIService(config, DeepSeekService.class);
}
/**
* 获取Doubao模型服务
*
* @param config 创建的AI服务模型的配置
* @return DoubaoService
* @since 6.0.0
*/
public static DoubaoService getDoubaoService(AIConfig config) {
return getAIService(config, DoubaoService.class);
}
/**
* 获取Grok模型服务
*
* @param config 创建的AI服务模型的配置
* @return GrokService
* @since 6.0.0
*/
public static GrokService getGrokService(AIConfig config) {
return getAIService(config, GrokService.class);
}
/**
* 获取Openai模型服务
*
* @param config 创建的AI服务模型的配置
* @return OpenAIService
* @since 6.0.0
*/
public static OpenaiService getOpenAIService(AIConfig config) {
return getAIService(config, OpenaiService.class);
}
/**
* AI大模型对话功能
*
* @param config 创建的AI服务模型的配置
* @param prompt 需要对话的内容
* @return AI模型返回的Response响应字符串
* @since 6.0.0
*/
public static String chat(AIConfig config, String prompt) {
return getAIService(config).chat(prompt);
}
/**
* AI大模型对话功能
*
* @param config 创建的AI服务模型的配置
* @param messages 由目前为止的对话组成的消息列表可以设置rolecontent详细参考官方文档
* @return AI模型返回的Response响应字符串
* @since 6.0.0
*/
public static String chat(AIConfig config, List<Message> messages) {
return getAIService(config).chat(messages);
}
}

View File

@ -0,0 +1,24 @@
package org.dromara.hutool.ai;
/**
* 模型厂商的名称不指具体的模型
*
* @author elichow
* @since 6.0.0
*/
public enum ModelName {
DEEPSEEK("deepSeek"),
OPENAI("openai"),
DOUBAO("doubao"),
GROK("grok");
private final String value;
ModelName(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}

View File

@ -0,0 +1,141 @@
package org.dromara.hutool.ai;
/**
* 各模型厂商包含的model指具体的模型
*
* @author elichow
* @since 6.0.0
*/
public class Models {
// DeepSeek的模型
public enum DeepSeek {
DEEPSEEK_CHAT("deepseek-chat"),
DEEPSEEK_REASONER("deepseek-reasoner");
private final String model;
DeepSeek(String model) {
this.model = model;
}
public String getModel() {
return model;
}
}
// Openai的模型
public enum Openai {
GPT_4_5_PREVIEW("gpt-4.5-preview"),
GPT_4O("gpt-4o"),
CHATGPT_4O_LATEST("chatgpt-4o-latest"),
GPT_4O_MINI("gpt-4o-mini"),
O1("o1"),
O1_MINI("o1-mini"),
O1_PREVIEW("o1-preview"),
O3_MINI("o3-mini"),
GPT_4O_REALTIME_PREVIEW("gpt-4o-realtime-preview"),
GPT_4O_MINI_REALTIME_PREVIEW("gpt-4o-mini-realtime-preview"),
GPT_4O_AUDIO_PREVIEW("gpt-4o-audio-preview"),
GPT_4O_MINI_AUDIO_PREVIEW("gpt-4o-mini-audio-preview"),
GPT_4_TURBO("gpt-4-turbo"),
GPT_4_TURBO_PREVIEW("gpt-4-turbo-preview"),
GPT_4("gpt-4"),
GPT_3_5_TURBO_0125("gpt-3.5-turbo-0125"),
GPT_3_5_TURBO("gpt-3.5-turbo"),
GPT_3_5_TURBO_1106("gpt-3.5-turbo-1106"),
GPT_3_5_TURBO_INSTRUCT("gpt-3.5-turbo-instruct"),
DALL_E_3("dall-e-3"),
DALL_E_2("dall-e-2"),
TTS_1("tts-1"),
TTS_1_HD("tts-1-hd"),
WHISPER_1("whisper-1"),
TEXT_EMBEDDING_3_LARGE("text-embedding-3-large"),
TEXT_EMBEDDING_3_SMALL("text-embedding-3-small"),
TEXT_EMBEDDING_ADA_002("text-embedding-ada-002"),
OMNI_MODERATION_LATEST("omni-moderation-latest"),
OMNI_MODERATION_2024_09_26("omni-moderation-2024-09-26"),
TEXT_MODERATION_LATEST("text-moderation-latest"),
TEXT_MODERATION_STABLE("text-moderation-stable"),
TEXT_MODERATION_007("text-moderation-007"),
BABBAGE_002("babbage-002"),
DAVINCI_002("davinci-002");
private final String model;
Openai(String model) {
this.model = model;
}
public String getModel() {
return model;
}
}
// Doubao的模型
public enum Doubao {
DOUBAO_1_5_PRO_32K("doubao-1.5-pro-32k-250115"),
DOUBAO_1_5_PRO_256K("doubao-1.5-pro-256k-250115"),
DOUBAO_1_5_LITE_32K("doubao-1.5-lite-32k-250115"),
DEEPSEEK_R1("deepseek-r1-250120"),
DEEPSEEK_R1_DISTILL_QWEN_32B("deepseek-r1-distill-qwen-32b-250120"),
DEEPSEEK_R1_DISTILL_QWEN_7B("deepseek-r1-distill-qwen-7b-250120"),
DEEPSEEK_V3("deepseek-v3-241226"),
DOUBAO_PRO_4K_240515("doubao-pro-4k-240515"),
DOUBAO_PRO_4K_CHARACTER_240728("doubao-pro-4k-character-240728"),
DOUBAO_PRO_4K_FUNCTIONCALL_240615("doubao-pro-4k-functioncall-240615"),
DOUBAO_PRO_4K_BROWSING_240524("doubao-pro-4k-browsing-240524"),
DOUBAO_PRO_32K_241215("doubao-pro-32k-241215"),
DOUBAO_PRO_32K_FUNCTIONCALL_241028("doubao-pro-32k-functioncall-241028"),
DOUBAO_PRO_32K_BROWSING_241115("doubao-pro-32k-browsing-241115"),
DOUBAO_PRO_32K_CHARACTER_241215("doubao-pro-32k-character-241215"),
DOUBAO_PRO_128K_240628("doubao-pro-128k-240628"),
DOUBAO_PRO_256K_240828("doubao-pro-256k-240828"),
DOUBAO_LITE_4K_240328("doubao-lite-4k-240328"),
DOUBAO_LITE_4K_PRETRAIN_CHARACTER_240516("doubao-lite-4k-pretrain-character-240516"),
DOUBAO_LITE_32K_240828("doubao-lite-32k-240828"),
DOUBAO_LITE_32K_CHARACTER_241015("doubao-lite-32k-character-241015"),
DOUBAO_LITE_128K_240828("240828"),
MOONSHOT_V1_8K("moonshot-v1-8k"),
MOONSHOT_V1_32K("moonshot-v1-32k"),
MOONSHOT_V1_128K("moonshot-v1-128k"),
CHATGLM3_130B_FC("chatglm3-130b-fc-v1.0"),
CHATGLM3_130_FIN("chatglm3-130-fin-v1.0-update"),
MISTRAL_7B("mistral-7b-instruct-v0.2"),
DOUBAO_1_5_VISION_PRO_32K("doubao-1.5-vision-pro-32k-250115"),
DOUBAO_VISION_PRO_32K("doubao-vision-pro-32k-241008"),
DOUBAO_VISION_LITE_32K("doubao-vision-lite-32k-241015"),
DOUBAO_EMBEDDING_LARGE("doubao-embedding-large-text-240915"),
DOUBAO_EMBEDDING_TEXT_240715("doubao-embedding-text-240715"),
DOUBAO_EMBEDDING_VISION("doubao-embedding-vision-241215");
private final String model;
Doubao(String model) {
this.model = model;
}
public String getModel() {
return model;
}
}
// Grok的模型
public enum Grok {
GROK_2_1212("grok-2-1212"),
GROK_2_VISION_1212("grok-2-vision-1212"),
GROK_BETA("grok-beta"),
GROK_VISION_BETA("grok-vision-beta");
private final String model;
Grok(String model) {
this.model = model;
}
public String getModel() {
return model;
}
}
}

View File

@ -0,0 +1,97 @@
package org.dromara.hutool.ai.core;
import java.util.Map;
/**
* AI配置类
*
* @author elichow
* @since 6.0.0
*/
public interface AIConfig {
/**
* 获取模型厂商名称
*
* @return 模型厂商名称
* @since 6.0.0
*/
default String getModelName() {
return this.getClass().getSimpleName();
}
/**
* 设置apiKey
*
* @param apiKey apiKey
* @since 6.0.0
*/
void setApiKey(String apiKey);
/**
* 获取apiKey
*
* @return apiKey
* @since 6.0.0
*/
String getApiKey();
/**
* 设置apiUrl
*
* @param apiUrl api请求地址
* @since 6.0.0
*/
void setApiUrl(String apiUrl);
/**
* 获取apiUrl
*
* @return apiUrl
* @since 6.0.0
*/
String getApiUrl();
/**
* 设置model
*
* @param model model
* @since 6.0.0
*/
void setModel(String model);
/**
* 返回model
*
* @return model
* @since 6.0.0
*/
String getModel();
/**
* 设置动态参数
*
* @param key 参数字段
* @param value 参数值
* @since 6.0.0
*/
void putAdditionalConfigByKey(String key, Object value);
/**
* 获取动态参数
*
* @param key 参数字段
* @return 参数值
* @since 6.0.0
*/
Object getAdditionalConfigByKey(String key);
/**
* 获取动态参数列表
*
* @return 参数列表Map
* @since 6.0.0
*/
Map<String, Object> getAdditionalConfigMap();
}

View File

@ -0,0 +1,102 @@
package org.dromara.hutool.ai.core;
import java.lang.reflect.Constructor;
/**
* 用于AIConfig的创建创建同时支持链式设置参数
*
* @author elichow
* @since 6.0.0
*/
public class AIConfigBuilder {
private final AIConfig config;
/**
* 构造
*
* @param modelName 模型厂商的名称注意不是指具体的模型
*/
public AIConfigBuilder(String modelName) {
try {
// 获取配置类
Class<? extends AIConfig> configClass = AIConfigRegistry.getConfigClass(modelName);
if (configClass == null) {
throw new IllegalArgumentException("Unsupported model: " + modelName);
}
// 使用反射创建实例
Constructor<? extends AIConfig> constructor = configClass.getDeclaredConstructor();
config = constructor.newInstance();
} catch (Exception e) {
throw new RuntimeException("Failed to create AIConfig instance", e);
}
}
/**
* 设置apiKey
*
* @param apiKey apiKey
* @return config
* @since 6.0.0
*/
public synchronized AIConfigBuilder setApiKey(String apiKey) {
if (apiKey != null) {
config.setApiKey(apiKey);
}
return this;
}
/**
* 设置AI模型请求API接口的地址不设置为默认值
*
* @param apiUrl API接口地址
* @return config
* @since 6.0.0
*/
public synchronized AIConfigBuilder setApiUrl(String apiUrl) {
if (apiUrl != null) {
config.setApiUrl(apiUrl);
}
return this;
}
/**
* 设置具体的model不设置为默认值
*
* @param model 具体model的名称
* @return config
* @since 6.0.0
*/
public synchronized AIConfigBuilder setModel(String model) {
if (model != null) {
config.setModel(model);
}
return this;
}
/**
* 动态设置Request请求体中的属性字段每个模型功能支持的字段请参照对应的官方文档
*
* @param key Request中的支持的属性名
* @param value 设置的属性值
* @return config
* @since 6.0.0
*/
public AIConfigBuilder putAdditionalConfig(String key, Object value) {
if (value != null) {
config.putAdditionalConfigByKey(key, value);
}
return this;
}
/**
* 返回config实例
*
* @return config
* @since 6.0.0
*/
public AIConfig build() {
return config;
}
}

View File

@ -0,0 +1,29 @@
package org.dromara.hutool.ai.core;
import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap;
import java.util.Map;
import java.util.ServiceLoader;
/**
* AIConfig实现类的加载器
*
* @author elichow
* @since 6.0.0
*/
public class AIConfigRegistry {
private static final Map<String, Class<? extends AIConfig>> configClasses = new SafeConcurrentHashMap<>();
// 加载所有 AIConfig 实现类
static {
ServiceLoader<AIConfig> loader = ServiceLoader.load(AIConfig.class);
for (AIConfig config : loader) {
configClasses.put(config.getModelName().toLowerCase(), config.getClass());
}
}
public static Class<? extends AIConfig> getConfigClass(String modelName) {
return configClasses.get(modelName.toLowerCase());
}
}

View File

@ -0,0 +1,31 @@
package org.dromara.hutool.ai.core;
import java.util.List;
/**
* 模型公共的API功能特有的功能在model.xx.XXService下定义
*
* @author elichow
* @since 6.0.0
*/
public interface AIService {
/**
* 对话
*
* @param prompt user题词
* @return AI回答
* @since 6.0.0
*/
String chat(String prompt);
/**
* 对话
*
* @param messages 由目前为止的对话组成的消息列表可以设置rolecontent详细参考官方文档
* @return AI回答
* @since 6.0.0
*/
String chat(List<Message> messages);
}

View File

@ -0,0 +1,28 @@
package org.dromara.hutool.ai.core;
/**
* 用于加载AI服务,每一个通过SPI创建的AI服务都要实现此接口
*
* @author elichow
* @since 6.0.0
*/
public interface AIServiceProvider {
/**
* 获取AI服务名称
*
* @return AI服务名称
* @since 6.0.0
*/
String getServiceName();
/**
* 创建AI服务实例
*
* @param config AIConfig配置
* @param <T> AIService类型
* @return AI服务实例
* @since 6.0.0
*/
<T extends AIService> T create(AIConfig config);
}

View File

@ -0,0 +1,72 @@
package org.dromara.hutool.ai.core;
import org.dromara.hutool.ai.AIException;
import org.dromara.hutool.http.HttpGlobalConfig;
import org.dromara.hutool.http.HttpUtil;
import org.dromara.hutool.http.client.Response;
import org.dromara.hutool.http.meta.HeaderName;
import org.dromara.hutool.http.meta.Method;
import java.util.Map;
/**
* 基础AIService包含基公共参数和公共方法
*
* @author elichow
* @since 6.0.0
*/
public class BaseAIService {
protected final AIConfig config;
public BaseAIService(AIConfig config) {
this.config = config;
}
protected Response sendGet(String endpoint) {
//链式构建请求
try {
//设置超时
HttpGlobalConfig.setTimeout(3000);
return HttpUtil.createRequest(config.getApiUrl() + endpoint, Method.GET)
.header(HeaderName.ACCEPT, "application/json")
.header(HeaderName.AUTHORIZATION, "Bearer " + config.getApiKey())
.send();
} catch (AIException e) {
throw new AIException("Failed to send GET request: " + e.getMessage(), e);
}
}
protected Response sendPost(String endpoint, String paramJson) {
//链式构建请求
try {
//设置超时
HttpGlobalConfig.setTimeout(3000);
return HttpUtil.createRequest(config.getApiUrl() + endpoint, Method.POST)
.header(HeaderName.CONTENT_TYPE, "application/json")
.header(HeaderName.ACCEPT, "application/json")
.header(HeaderName.AUTHORIZATION, "Bearer " + config.getApiKey())
.body(paramJson)
.send();
} catch (AIException e) {
throw new AIException("Failed to send POST request" + e.getMessage(), e);
}
}
protected Response sendFormData(String endpoint, Map<String, Object> paramMap) {
//链式构建请求
try {
//设置超时
HttpGlobalConfig.setTimeout(3000);
return HttpUtil.createPost(config.getApiUrl() + endpoint)
//form表单中有file对象会自动将文件编码为 multipart/form-data 格式不需要设置
// .header(HeaderName.CONTENT_TYPE, "multipart/form-data")
.header(HeaderName.AUTHORIZATION, "Bearer " + config.getApiKey())
.form(paramMap)
.send();
} catch (AIException e) {
throw new AIException("Failed to send POST request" + e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,70 @@
package org.dromara.hutool.ai.core;
import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap;
import java.util.Map;
/**
* Config基础类定义模型配置的基本属性
*
* @author elichow
* @since 6.0.0
*/
public class BaseConfig implements AIConfig {
//apiKey
protected volatile String apiKey;
//API请求地址
protected volatile String apiUrl;
//具体模型
protected volatile String model;
//动态扩展字段
protected Map<String, Object> additionalConfig = new SafeConcurrentHashMap<>();
@Override
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
@Override
public String getApiKey() {
return apiKey;
}
@Override
public void setApiUrl(String apiUrl) {
this.apiUrl = apiUrl;
}
@Override
public String getApiUrl() {
return apiUrl;
}
@Override
public void setModel(String model) {
this.model = model;
}
@Override
public String getModel() {
return model;
}
@Override
public void putAdditionalConfigByKey(String key, Object value) {
this.additionalConfig.put(key, value);
}
@Override
public Object getAdditionalConfigByKey(String key) {
return additionalConfig.get(key);
}
@Override
public Map<String, Object> getAdditionalConfigMap() {
return new SafeConcurrentHashMap<>(additionalConfig);
}
}

View File

@ -0,0 +1,27 @@
package org.dromara.hutool.ai.core;
/**
* 公共Message类
*
* @author elichow
* @since 6.0.0
*/
public class Message {
//角色 注意如果设置系统消息请放在messages列表的第一位
private final String role;
//内容
private final Object content;
public Message(String role, Object content) {
this.role = role;
this.content = content;
}
public String getRole() {
return role;
}
public Object getContent() {
return content;
}
}

View File

@ -0,0 +1,11 @@
package org.dromara.hutool.ai.model.deepseek;
/**
* deepSeek公共类
*
* @author elichow
* @since 6.0.0
*/
public class DeepSeekCommon {
}

View File

@ -0,0 +1,33 @@
package org.dromara.hutool.ai.model.deepseek;
import org.dromara.hutool.ai.Models;
import org.dromara.hutool.ai.core.BaseConfig;
/**
* DeepSeek配置类初始化API接口地址设置默认的模型
*
* @author elichow
* @since 6.0.0
*/
public class DeepSeekConfig extends BaseConfig {
private final String API_URL = "https://api.deepseek.com";
private final String DEFAULT_MODEL = Models.DeepSeek.DEEPSEEK_CHAT.getModel();
public DeepSeekConfig() {
setApiUrl(API_URL);
setModel(DEFAULT_MODEL);
}
public DeepSeekConfig(String apiKey) {
this();
setApiKey(apiKey);
}
@Override
public String getModelName() {
return "deepSeek";
}
}

View File

@ -0,0 +1,24 @@
package org.dromara.hutool.ai.model.deepseek;
import org.dromara.hutool.ai.core.AIConfig;
import org.dromara.hutool.ai.core.AIServiceProvider;
/**
* 创建DeepSeek服务实现类
*
* @author elichow
* @since 6.0.0
*/
public class DeepSeekProvider implements AIServiceProvider {
@Override
public String getServiceName() {
return "deepSeek";
}
@Override
public DeepSeekService create(AIConfig config) {
return new DeepSeekServiceImpl(config);
}
}

View File

@ -0,0 +1,37 @@
package org.dromara.hutool.ai.model.deepseek;
import org.dromara.hutool.ai.core.AIService;
/**
* deepSeek支持的扩展接口
*
* @author elichow
* @since 6.0.0
*/
public interface DeepSeekService extends AIService {
/**
* 模型beta功能
*
* @param prompt 题词
* @return AI的回答
* @since 6.0.0
*/
String beta(String prompt);
/**
* 列出所有模型列表
*
* @return model列表
* @since 6.0.0
*/
String models();
/**
* 查询余额
*
* @return 余额
* @since 6.0.0
*/
String balance();
}

View File

@ -0,0 +1,96 @@
package org.dromara.hutool.ai.model.deepseek;
import org.dromara.hutool.ai.core.AIConfig;
import org.dromara.hutool.ai.core.BaseAIService;
import org.dromara.hutool.ai.core.Message;
import org.dromara.hutool.http.client.Response;
import org.dromara.hutool.json.JSONUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* DeepSeek服务AI具体功能的实现
*
* @author elichow
* @since 6.0.0
*/
public class DeepSeekServiceImpl extends BaseAIService implements DeepSeekService {
//对话补全
private final String CHAT_ENDPOINT = "/chat/completions";
//FIM补全beta
private final String BETA_ENDPOINT = "/beta/completions";
//列出模型
private final String MODELS_ENDPOINT = "/models";
//余额查询
private final String BALANCE_ENDPOINT = "/user/balance";
public DeepSeekServiceImpl(AIConfig config) {
//初始化DeepSeek客户端
super(config);
}
@Override
public String chat(String prompt) {
// 定义消息结构
List<Message> messages = new ArrayList<>();
messages.add(new Message("system", "You are a helpful assistant"));
messages.add(new Message("user", prompt));
return chat(messages);
}
@Override
public String chat(List<Message> messages) {
String paramJson = buildChatRequestBody(messages);
Response response = sendPost(CHAT_ENDPOINT, paramJson);
return response.bodyStr();
}
@Override
public String beta(String prompt) {
String paramJson = buildBetaRequestBody(prompt);
Response response = sendPost(BETA_ENDPOINT, paramJson);
return response.bodyStr();
}
@Override
public String models() {
Response response = sendGet(MODELS_ENDPOINT);
return response.bodyStr();
}
@Override
public String balance() {
Response response = sendGet(BALANCE_ENDPOINT);
return response.bodyStr();
}
// 构建chat请求体
private String buildChatRequestBody(List<Message> messages) {
//使用JSON工具
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("messages", messages);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
// 构建beta请求体
private String buildBetaRequestBody(String prompt) {
// 定义消息结构
//使用JSON工具
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("prompt", prompt);
// //合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
}

View File

@ -0,0 +1,95 @@
package org.dromara.hutool.ai.model.doubao;
/**
* doubao公共类
*
* @author elichow
* @since 6.0.0
*/
public class DoubaoCommon {
//doubao上下文缓存参数
public enum DoubaoContext {
SESSION("session"),
COMMON_PREFIX("common_prefix");
private final String mode;
DoubaoContext(String mode) {
this.mode = mode;
}
public String getMode() {
return mode;
}
}
//豆包视觉参数
public enum DoubaoVision {
AUTO("auto"),
LOW("low"),
HIGH("high");
private final String detail;
DoubaoVision(String detail) {
this.detail = detail;
}
public String getDetail() {
return detail;
}
}
//doubao视频生成参数
public enum DoubaoVideo {
//宽高比例
RATIO_16_9("--rt", "16:9"),//[1280, 720]
RATIO_4_3("--rt", "4:3"),//[960, 720]
RATIO_1_1("--rt", "1:1"),//[720, 720]
RATIO_3_4("--rt", "3:4"),//[720, 960]
RATIO_9_16("--rt", "9:16"),//[720, 1280]
RATIO_21_9("--rt", "21:9"),//[1280, 544]
//生成视频时长
DURATION_5("--dur", 5),//文生视频图生视频
DURATION_10("--dur", 10),//文生视频
//帧率即一秒时间内视频画面数量
FPS_5("--fps", 24),
//视频分辨率
RESOLUTION_5("--rs", "720p"),
//生成视频是否包含水印
WATERMARK_TRUE("--wm", true),
WATERMARK_FALSE("--wm", false);
private final String type;
private final Object value;
DoubaoVideo(String type, Object value) {
this.type = type;
this.value = value;
}
public String getType() {
return type;
}
public Object getValue() {
if (value instanceof String) {
return (String) value;
} else if (value instanceof Integer) {
return (Integer) value;
} else if (value instanceof Boolean) {
return (Boolean) value;
}
return value;
}
}
}

View File

@ -0,0 +1,33 @@
package org.dromara.hutool.ai.model.doubao;
import org.dromara.hutool.ai.Models;
import org.dromara.hutool.ai.core.BaseConfig;
/**
* Doubao配置类初始化API接口地址设置默认的模型
*
* @author elichow
* @since 6.0.0
*/
public class DoubaoConfig extends BaseConfig {
private final String API_URL = "https://ark.cn-beijing.volces.com/api/v3";
private final String DEFAULT_MODEL = Models.Doubao.DOUBAO_1_5_LITE_32K.getModel();
public DoubaoConfig() {
setApiUrl(API_URL);
setModel(DEFAULT_MODEL);
}
public DoubaoConfig(String apiKey) {
this();
setApiKey(apiKey);
}
@Override
public String getModelName() {
return "doubao";
}
}

View File

@ -0,0 +1,23 @@
package org.dromara.hutool.ai.model.doubao;
import org.dromara.hutool.ai.core.AIConfig;
import org.dromara.hutool.ai.core.AIServiceProvider;
/**
* 创建Doubap服务实现类
*
* @author elichow
* @since 6.0.0
*/
public class DoubaoProvider implements AIServiceProvider {
@Override
public String getServiceName() {
return "doubao";
}
@Override
public DoubaoService create(AIConfig config) {
return new DoubaoServiceImpl(config);
}
}

View File

@ -0,0 +1,179 @@
package org.dromara.hutool.ai.model.doubao;
import org.dromara.hutool.ai.core.AIService;
import org.dromara.hutool.ai.core.Message;
import java.util.List;
/**
* doubao支持的扩展接口
*
* @author elichow
* @since 6.0.0
*/
public interface DoubaoService extends AIService {
/**
* 图像理解模型会依据传入的图片信息以及问题给出回复
*
* @param prompt 提问
* @param images 图片列表/或者图片Base64编码图片列表(URI形式)
* @param detail 手动设置图片的质量取值范围highlowauto,默认为auto
* @return AI回答
* @since 6.0.0
*/
String chatVision(String prompt, List<String> images, String detail);
/**
* 图像理解模型会依据传入的图片信息以及问题给出回复
*
* @param prompt 提问
* @param images 传入的图片列表地址/或者图片Base64编码图片列表(URI形式)
* @return AI回答
* @since 6.0.0
*/
default String chatVision(String prompt, List<String> images) {
return chatVision(prompt, images, DoubaoCommon.DoubaoVision.AUTO.getDetail());
}
/**
* 创建视频生成任务
* 注意调用该方法时配置config中的model为您创建的推理接入点EndpointID详细参考官方文档
*
* @param text 文本提示词
* @param image 图片/或者图片Base64编码图片(URI形式)
* @param videoParams 视频参数列表
* @return 生成任务id
* @since 6.0.0
*/
String videoTasks(String text, String image, List<DoubaoCommon.DoubaoVideo> videoParams);
/**
* 创建视频生成任务
* 注意调用该方法时配置config中的model为您创建的推理接入点EndpointID详细参考官方文档
*
* @param text 文本提示词
* @param image 图片/或者图片Base64编码图片(URI形式)
* @return 生成任务id
* @since 6.0.0
*/
default String videoTasks(String text, String image) {
return videoTasks(text, image, null);
}
/**
* 查询视频生成任务信息
*
* @param taskId 通过创建生成视频任务返回的生成任务id
* @return 生成任务信息
* @since 6.0.0
*/
String getVideoTasksInfo(String taskId);
/**
* 文本向量化
*
* @param input 需要向量化的内容列表支持中文英文
* @return 处理后的向量信息
* @since 6.0.0
*/
String embeddingText(String[] input);
/**
* 图文向量化仅支持单一文本单张图片或文本与图片的组合输入即一段文本 + 一张图片暂不支持批量文本 / 图片的同时处理
*
* @param text 需要向量化的内容
* @param image 需要向量化的图片地址/或者图片Base64编码图片(URI形式)
* @return 处理后的向量信息
* @since 6.0.0
*/
String embeddingVision(String text, String image);
/**
* 应用(Bot) config中model设置为您创建的应用ID
*
* @param messages 由对话组成的消息列表如系统人设背景信息等用户自定义的信息
* @return AI回答
* @since 6.0.0
*/
String botsChat(List<Message> messages);
/**
* 分词可以将文本转换为模型可理解的 token id并返回文本的 tokens 数量token id token 在原始文本中的偏移量等信息
*
* @param text 需要分词的内容列表
* @return 分词结果
* @since 6.0.0
*/
String tokenization(String[] text);
/**
* 批量推理 Chat
* 注意调用该方法时配置config中的model为您创建的批量推理接入点EndpointID详细参考官方文档
* 该方法不支持流式
*
* @param prompt chat内容
* @return AI回答
* @since 6.0.0
*/
String batchChat(String prompt);
/**
* 批量推理 Chat
* 注意调用该方法时配置config中的model为您创建的批量推理接入点EndpointID详细参考官方文档
* 该方法不支持流式
*
* @param messages 由对话组成的消息列表如系统人设背景信息等用户自定义的信息
* @return AI回答
* @since 6.0.0
*/
String batchChat(List<Message> messages);
/**
* 创建上下文缓存 创建上下文缓存获得缓存 id字段后在上下文缓存对话 API中使用
* 注意调用该方法时配置config中的model为您创建的推理接入点EndpointID,
* 推理接入点中使用的模型需要在模型管理中开启缓存功能详细参考官方文档
*
* @param messages 由对话组成的消息列表如系统人设背景信息等用户自定义的信息
* @param mode 上下文缓存的类型,详细参考官方文档 默认为session
* @return 返回的缓存id
* @since 6.0.0
*/
String createContext(List<Message> messages, String mode);
/**
* 创建上下文缓存 创建上下文缓存获得缓存 id字段后在上下文缓存对话 API中使用
* 注意调用该方法时配置config中的model为您创建的推理接入点EndpointID,
* 推理接入点中使用的模型需要在模型管理中开启缓存功能详细参考官方文档
*
* @param messages 由对话组成的消息列表如系统人设背景信息等用户自定义的信息
* @return 返回的缓存id
* @since 6.0.0
*/
default String createContext(List<Message> messages) {
return createContext(messages, DoubaoCommon.DoubaoContext.SESSION.getMode());
}
/**
* 上下文缓存对话 向大模型发起带上下文缓存的请求
* 注意配置config中的model可以为您创建的推理接入点EndpointID也可以是支持chat的model
*
* @param prompt 对话的内容题词
* @param contextId 创建上下文缓存后获取的缓存id
* @return AI的回答
* @since 6.0.0
*/
String chatContext(String prompt, String contextId);
/**
* 上下文缓存对话 向大模型发起带上下文缓存的请求
* 注意配置config中的model可以为您创建的推理接入点EndpointID也可以是支持chat的model
*
* @param messages 对话的信息 不支持最后一个元素的role设置为assistant如使用session 缓存mode设置为session传入最新一轮对话的信息无需传入历史信息
* @param contextId 创建上下文缓存后获取的缓存id
* @return AI的回答
* @since 6.0.0
*/
String chatContext(List<Message> messages, String contextId);
}

View File

@ -0,0 +1,341 @@
package org.dromara.hutool.ai.model.doubao;
import org.dromara.hutool.ai.core.AIConfig;
import org.dromara.hutool.ai.core.BaseAIService;
import org.dromara.hutool.ai.core.Message;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.http.client.Response;
import org.dromara.hutool.json.JSONUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Doubao服务AI具体功能的实现
*
* @author elichow
* @since 6.0.0
*/
public class DoubaoServiceImpl extends BaseAIService implements DoubaoService {
//对话
private final String CHAT_ENDPOINT = "/chat/completions";
//文本向量化
private final String EMBEDDING_TEXT = "/embeddings";
//图文向量化
private final String EMBEDDING_VISION = "/embeddings/multimodal";
//应用bots
private final String BOTS_CHAT = "/bots/chat/completions";
//分词
private final String TOKENIZATION = "/tokenization";
//批量推理chat
private final String BATCH_CHAT = "/batch/chat/completions";
//创建上下文缓存
private final String CREATE_CONTEXT = "/context/create";
//上下文缓存对话
private final String CHAT_CONTEXT = "/context/chat/completions";
//创建视频生成任务
private final String CREATE_VIDEO = "/contents/generations/tasks";
public DoubaoServiceImpl(AIConfig config) {
//初始化doubao客户端
super(config);
}
@Override
public String chat(String prompt) {
// 定义消息结构
List<Message> messages = new ArrayList<>();
messages.add(new Message("system", "You are a helpful assistant"));
messages.add(new Message("user", prompt));
return chat(messages);
}
@Override
public String chat(List<Message> messages) {
String paramJson = buildChatRequestBody(messages);
Response response = sendPost(CHAT_ENDPOINT, paramJson);
return response.bodyStr();
}
@Override
public String chatVision(String prompt, List<String> images, String detail) {
String paramJson = buildChatVisionRequestBody(prompt, images, detail);
Response response = sendPost(CHAT_ENDPOINT, paramJson);
return response.bodyStr();
}
@Override
public String videoTasks(String text, String image, List<DoubaoCommon.DoubaoVideo> videoParams) {
String paramJson = buildGenerationsTasksRequestBody(text, image, videoParams);
Response response = sendPost(CREATE_VIDEO, paramJson);
return response.bodyStr();
}
@Override
public String getVideoTasksInfo(String taskId) {
Response response = sendGet(CREATE_VIDEO + "/" + taskId);
return response.bodyStr();
}
@Override
public String embeddingText(String[] input) {
String paramJson = buildEmbeddingTextRequestBody(input);
Response response = sendPost(EMBEDDING_TEXT, paramJson);
return response.bodyStr();
}
@Override
public String embeddingVision(String text, String image) {
String paramJson = buildEmbeddingVisionRequestBody(text, image);
Response response = sendPost(EMBEDDING_VISION, paramJson);
return response.bodyStr();
}
@Override
public String botsChat(List<Message> messages) {
String paramJson = buildBotsChatRequestBody(messages);
System.out.println(paramJson);
Response response = sendPost(BOTS_CHAT, paramJson);
return response.bodyStr();
}
@Override
public String tokenization(String[] text) {
String paramJson = buildTokenizationRequestBody(text);
Response response = sendPost(TOKENIZATION, paramJson);
return response.bodyStr();
}
@Override
public String batchChat(String prompt) {
// 定义消息结构
List<Message> messages = new ArrayList<>();
messages.add(new Message("system", "You are a helpful assistant"));
messages.add(new Message("user", prompt));
return batchChat(messages);
}
@Override
public String batchChat(List<Message> messages) {
String paramJson = buildBatchChatRequestBody(messages);
System.out.println(paramJson);
Response response = sendPost(BATCH_CHAT, paramJson);
return response.bodyStr();
}
@Override
public String createContext(List<Message> messages, String mode) {
String paramJson = buildCreateContextRequest(messages, mode);
System.out.println(paramJson);
Response response = sendPost(CREATE_CONTEXT, paramJson);
return response.bodyStr();
}
@Override
public String chatContext(String prompt, String contextId) {
// 定义消息结构
List<Message> messages = new ArrayList<>();
messages.add(new Message("user", prompt));
return chatContext(messages, contextId);
}
@Override
public String chatContext(List<Message> messages, String contextId) {
String paramJson = buildChatContentRequestBody(messages, contextId);
Response response = sendPost(CHAT_CONTEXT, paramJson);
return response.bodyStr();
}
// 构建chat请求体
private String buildChatRequestBody(List<Message> messages) {
//使用JSON工具
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("messages", messages);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
//构建chatVision请求体
private String buildChatVisionRequestBody(String prompt, List<String> images, String detail) {
// 定义消息结构
List<Message> messages = new ArrayList<>();
List<Object> content = new ArrayList<>();
Map<String, String> contentMap = new HashMap<>();
contentMap.put("type", "text");
contentMap.put("text", prompt);
content.add(contentMap);
for (String img : images) {
HashMap<String, Object> imgUrlMap = new HashMap<>();
imgUrlMap.put("type", "image_url");
HashMap<String, String> urlMap = new HashMap<>();
urlMap.put("url", img);
urlMap.put("detail", detail);
imgUrlMap.put("image_url", urlMap);
content.add(imgUrlMap);
}
messages.add(new Message("user", content));
//使用JSON工具
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("messages", messages);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
//构建文本向量化请求体
private String buildEmbeddingTextRequestBody(String[] input) {
//使用JSON工具
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("input", input);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
//构建图文向量化请求体
private String buildEmbeddingVisionRequestBody(String text, String image) {
//使用JSON工具
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
List<Object> input = new ArrayList<>();
//添加文本参数
if (!StrUtil.isBlank(text)) {
Map<String, String> textMap = new HashMap<>();
textMap.put("type", "text");
textMap.put("text", text);
input.add(textMap);
}
//添加图片参数
if (!StrUtil.isBlank(image)) {
HashMap<String, Object> imgUrlMap = new HashMap<>();
imgUrlMap.put("type", "image_url");
HashMap<String, String> urlMap = new HashMap<>();
urlMap.put("url", image);
imgUrlMap.put("image_url", urlMap);
input.add(imgUrlMap);
}
paramMap.put("input", input);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
//构建应用chat请求体
private String buildBotsChatRequestBody(List<Message> messages) {
return buildChatRequestBody(messages);
}
//构建分词请求体
private String buildTokenizationRequestBody(String[] text) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("text", text);
return JSONUtil.toJsonStr(paramMap);
}
//构建批量推理chat请求体
private String buildBatchChatRequestBody(List<Message> messages) {
return buildChatRequestBody(messages);
}
//构建创建上下文缓存请求体
private String buildCreateContextRequest(List<Message> messages, String mode) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("messages", messages);
paramMap.put("model", config.getModel());
paramMap.put("mode", mode);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
//构建上下文缓存对话请求体
private String buildChatContentRequestBody(List<Message> messages, String contextId) {
//使用JSON工具
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("messages", messages);
paramMap.put("context_id", contextId);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
//构建创建视频任务请求体
private String buildGenerationsTasksRequestBody(String text, String image, List<DoubaoCommon.DoubaoVideo> videoParams) {
//使用JSON工具
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
List<Object> content = new ArrayList<>();
//添加文本参数
Map<String, String> textMap = new HashMap<>();
if (!StrUtil.isBlank(text)) {
textMap.put("type", "text");
textMap.put("text", text);
content.add(textMap);
}
//添加图片参数
if (!StrUtil.isNotBlank(image)) {
Map<String, Object> imgUrlMap = new HashMap<>();
imgUrlMap.put("type", "image_url");
HashMap<String, String> urlMap = new HashMap<>();
urlMap.put("url", image);
imgUrlMap.put("image_url", urlMap);
content.add(imgUrlMap);
}
//添加视频参数
if (videoParams != null && !videoParams.isEmpty()) {
//如果有文本参数就加在后面
if (textMap != null && !textMap.isEmpty()) {
int textIndex = content.indexOf(textMap);
StringBuilder textBuilder = new StringBuilder(text);
for (DoubaoCommon.DoubaoVideo videoParam : videoParams) {
textBuilder.append(" ").append(videoParam.getType()).append(" ").append(videoParam.getValue());
}
textMap.put("type", "text");
textMap.put("text", textBuilder.toString());
if (textIndex != -1) {
content.set(textIndex, textMap);
} else {
content.add(textMap);
}
} else {
//如果没有文本参数就重新增加
StringBuilder textBuilder = new StringBuilder();
for (DoubaoCommon.DoubaoVideo videoParam : videoParams) {
textBuilder.append(videoParam.getType()).append(videoParam.getValue()).append(" ");
}
textMap.put("type", "text");
textMap.put("text", textBuilder.toString());
content.add(textMap);
}
}
paramMap.put("content", content);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
}

View File

@ -0,0 +1,28 @@
package org.dromara.hutool.ai.model.grok;
/**
* grok公共类
*
* @author elichow
* @since 6.0.0
*/
public class GrokCommon {
//grok视觉参数
public enum GrokVision {
AUTO("auto"),
LOW("low"),
HIGH("high");
private final String detail;
GrokVision(String detail) {
this.detail = detail;
}
public String getDetail() {
return detail;
}
}
}

View File

@ -0,0 +1,34 @@
package org.dromara.hutool.ai.model.grok;
import org.dromara.hutool.ai.Models;
import org.dromara.hutool.ai.core.BaseConfig;
/**
* Grok配置类初始化API接口地址设置默认的模型
*
* @author elichow
* @since 6.0.0
*/
public class GrokConfig extends BaseConfig {
private final String API_URL = "https://api.x.ai/v1";
private final String DEFAULT_MODEL = Models.Grok.GROK_2_1212.getModel();
public GrokConfig() {
setApiUrl(API_URL);
setModel(DEFAULT_MODEL);
}
public GrokConfig(String apiKey) {
this();
setApiKey(apiKey);
}
@Override
public String getModelName() {
return "grok";
}
}

View File

@ -0,0 +1,23 @@
package org.dromara.hutool.ai.model.grok;
import org.dromara.hutool.ai.core.AIConfig;
import org.dromara.hutool.ai.core.AIServiceProvider;
/**
* 创建Grok服务实现类
*
* @author elichow
* @since 6.0.0
*/
public class GrokProvider implements AIServiceProvider {
@Override
public String getServiceName() {
return "grok";
}
@Override
public GrokService create(AIConfig config) {
return new GrokServiceImpl(config);
}
}

View File

@ -0,0 +1,99 @@
package org.dromara.hutool.ai.model.grok;
import org.dromara.hutool.ai.core.AIService;
import java.util.List;
/**
* grok支持的扩展接口
*
* @author elichow
* @since 6.0.0
*/
public interface GrokService extends AIService {
/**
* 创建消息回复
*
* @param prompt 题词
* @param maxToken 最大token
* @return AI回答
* @since 6.0.0
*/
String message(String prompt, int maxToken);
/**
* 图像理解模型会依据传入的图片信息以及问题给出回复
*
* @param prompt 题词
* @param images 图片列表/或者图片Base64编码图片列表(URI形式)
* @param detail 手动设置图片的质量取值范围highlowauto,默认为auto
* @return AI回答
* @since 6.0.0
*/
String chatVision(String prompt, List<String> images, String detail);
/**
* 图像理解模型会依据传入的图片信息以及问题给出回复
*
* @param prompt 题词
* @param images 传入的图片列表地址/或者图片Base64编码图片列表(URI形式)
* @return AI回答
* @since 6.0.0
*/
default String chatVision(String prompt, List<String> images) {
return chatVision(prompt, images, GrokCommon.GrokVision.AUTO.getDetail());
}
/**
* 列出所有model列表
*
* @return model列表
* @since 6.0.0
*/
String models();
/**
* 获取模型信息
*
* @param modelId model ID
* @return model信息
* @since 6.0.0
*/
String getModel(String modelId);
/**
* 列出所有语言model
*
* @return languageModel列表
* @since 6.0.0
*/
String languageModels();
/**
* 获取语言模型信息
*
* @param modelId model ID
* @return model信息
* @since 6.0.0
*/
String getLanguageModel(String modelId);
/**
* 分词可以将文本转换为模型可理解的 token 信息
*
* @param text 需要分词的内容
* @return 分词结果
* @since 6.0.0
*/
String tokenizeText(String text);
/**
* 从延迟对话中获取结果
*
* @param requestId 延迟对话中的延迟请求ID
* @return AI回答
* @since 6.0.0
*/
String deferredCompletion(String requestId);
}

View File

@ -0,0 +1,177 @@
package org.dromara.hutool.ai.model.grok;
import org.dromara.hutool.ai.core.AIConfig;
import org.dromara.hutool.ai.core.BaseAIService;
import org.dromara.hutool.ai.core.Message;
import org.dromara.hutool.http.client.Response;
import org.dromara.hutool.json.JSONUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Grok服务AI具体功能的实现
*
* @author elichow
* @since 6.0.0
*/
public class GrokServiceImpl extends BaseAIService implements GrokService {
//对话补全
private final String CHAT_ENDPOINT = "/chat/completions";
//创建消息回复
private final String MESSAGES = "/messages";
//列出模型
private final String MODELS_ENDPOINT = "/models";
//列出语言模型
private final String LANGUAGE_MODELS = "/language-models";
//分词
private final String TOKENIZE_TEXT = "/tokenize-text";
//获取延迟对话
private final String DEFERRED_COMPLETION = "/chat/deferred-completion";
public GrokServiceImpl(AIConfig config) {
//初始化grok客户端
super(config);
}
@Override
public String chat(String prompt) {
// 定义消息结构
List<Message> messages = new ArrayList<>();
messages.add(new Message("system", "You are a helpful assistant"));
messages.add(new Message("user", prompt));
return chat(messages);
}
@Override
public String chat(List<Message> messages) {
String paramJson = buildChatRequestBody(messages);
Response response = sendPost(CHAT_ENDPOINT, paramJson);
return response.bodyStr();
}
@Override
public String message(String prompt, int maxToken) {
// 定义消息结构
List<Message> messages = new ArrayList<>();
messages.add(new Message("system", "You are a helpful assistant"));
messages.add(new Message("user", prompt));
String paramJson = buildMessageRequestBody(messages, maxToken);
Response response = sendPost(MESSAGES, paramJson);
return response.bodyStr();
}
@Override
public String chatVision(String prompt, List<String> images, String detail) {
String paramJson = buildChatVisionRequestBody(prompt, images, detail);
Response response = sendPost(CHAT_ENDPOINT, paramJson);
return response.bodyStr();
}
@Override
public String models() {
Response response = sendGet(MODELS_ENDPOINT);
return response.bodyStr();
}
@Override
public String getModel(String modelId) {
Response response = sendGet(MODELS_ENDPOINT + "/" + modelId);
return response.bodyStr();
}
@Override
public String languageModels() {
Response response = sendGet(LANGUAGE_MODELS);
return response.bodyStr();
}
@Override
public String getLanguageModel(String modelId) {
Response response = sendGet(LANGUAGE_MODELS + "/" + modelId);
return response.bodyStr();
}
@Override
public String tokenizeText(String text) {
String paramJson = buildTokenizeRequestBody(text);
Response response = sendPost(TOKENIZE_TEXT, paramJson);
return response.bodyStr();
}
@Override
public String deferredCompletion(String requestId) {
Response response = sendGet(DEFERRED_COMPLETION + "/" + requestId);
return response.bodyStr();
}
// 构建chat请求体
private String buildChatRequestBody(List<Message> messages) {
//使用JSON工具
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("messages", messages);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
//构建chatVision请求体
private String buildChatVisionRequestBody(String prompt, List<String> images, String detail) {
// 定义消息结构
List<Message> messages = new ArrayList<>();
List<Object> content = new ArrayList<>();
Map<String, String> contentMap = new HashMap<>();
contentMap.put("type", "text");
contentMap.put("text", prompt);
content.add(contentMap);
for (String img : images) {
HashMap<String, Object> imgUrlMap = new HashMap<>();
imgUrlMap.put("type", "image_url");
HashMap<String, String> urlMap = new HashMap<>();
urlMap.put("url", img);
urlMap.put("detail", detail);
imgUrlMap.put("image_url", urlMap);
content.add(imgUrlMap);
}
messages.add(new Message("user", content));
//使用JSON工具
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("messages", messages);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
//构建消息回复请求体
private String buildMessageRequestBody(List<Message> messages, int maxToken) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("messages", messages);
paramMap.put("max_tokens", maxToken);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
//构建分词请求体
private String buildTokenizeRequestBody(String text) {
//使用JSON工具
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("text", text);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
}

View File

@ -0,0 +1,70 @@
package org.dromara.hutool.ai.model.openai;
/**
* openai公共类
*
* @author elichow
* @since 6.0.0
*/
public class OpenaiCommon {
//openai推理参数
public enum OpenaiReasoning {
LOW("low"),
MEDIUM("medium"),
HIGH("high");
private final String effort;
OpenaiReasoning(String effort) {
this.effort = effort;
}
public String getEffort() {
return effort;
}
}
//openai视觉参数
public enum OpenaiVision {
AUTO("auto"),
LOW("low"),
HIGH("high");
private final String detail;
OpenaiVision(String detail) {
this.detail = detail;
}
public String getDetail() {
return detail;
}
}
//openai音频参数
public enum OpenaiSpeech {
ALLOY("alloy"),
ASH("ash"),
CORAL("coral"),
ECHO("echo"),
FABLE("fable"),
ONYX("onyx"),
NOVA("nova"),
SAGE("sage"),
SHIMMER("shimmer");
private final String voice;
OpenaiSpeech(String voice) {
this.voice = voice;
}
public String getVoice() {
return voice;
}
}
}

View File

@ -0,0 +1,34 @@
package org.dromara.hutool.ai.model.openai;
import org.dromara.hutool.ai.Models;
import org.dromara.hutool.ai.core.BaseConfig;
/**
* openai配置类初始化API接口地址设置默认的模型
*
* @author elichow
* @since 6.0.0
*/
public class OpenaiConfig extends BaseConfig {
private final String API_URL = "https://api.openai.com/v1";
private final String DEFAULT_MODEL = Models.Openai.GPT_4O.getModel();
public OpenaiConfig() {
setApiUrl(API_URL);
setModel(DEFAULT_MODEL);
}
public OpenaiConfig(String apiKey) {
this();
setApiKey(apiKey);
}
@Override
public String getModelName() {
return "openai";
}
}

View File

@ -0,0 +1,24 @@
package org.dromara.hutool.ai.model.openai;
import org.dromara.hutool.ai.core.AIConfig;
import org.dromara.hutool.ai.core.AIServiceProvider;
/**
* 创建Openai服务实现类
*
* @author elichow
* @since 6.0.0
*/
public class OpenaiProvider implements AIServiceProvider {
@Override
public String getServiceName() {
return "openai";
}
@Override
public OpenaiService create(AIConfig config) {
return new OpenaiServiceImpl(config);
}
}

View File

@ -0,0 +1,190 @@
package org.dromara.hutool.ai.model.openai;
import org.dromara.hutool.ai.core.AIService;
import org.dromara.hutool.ai.core.Message;
import java.io.File;
import java.io.InputStream;
import java.util.List;
/**
* openai支持的扩展接口
*
* @author elichow
* @since 6.0.0
*/
public interface OpenaiService extends AIService {
/**
* 图像理解模型会依据传入的图片信息以及问题给出回复
*
* @param prompt 题词
* @param images 图片列表/或者图片Base64编码图片列表(URI形式)
* @param detail 手动设置图片的质量取值范围highlowauto,默认为auto
* @return AI回答
* @since 6.0.0
*/
String chatVision(String prompt, List<String> images, String detail);
/**
* 图像理解模型会依据传入的图片信息以及问题给出回复
*
* @param prompt 题词
* @param images 传入的图片列表地址/或者图片Base64编码图片列表(URI形式)
* @return AI回答
* @since 6.0.0
*/
default String chatVision(String prompt, List<String> images) {
return chatVision(prompt, images, OpenaiCommon.OpenaiVision.AUTO.getDetail());
}
/**
* 文生图 请设置config中model为支持图片功能的模型 DALL·E系列
*
* @param prompt 题词
* @return 包含生成图片的url
* @since 6.0.0
*/
String imagesGenerations(String prompt);
/**
* 图片编辑 该方法仅支持 DALL·E 2 model
*
* @param prompt 题词
* @param image 需要编辑的图像必须是 PNG 格式
* @param mask 如果提供则是一个与编辑图像大小相同的遮罩图像应该是灰度图白色表示需要编辑的区域黑色表示不需要编辑的区域
* @return 包含生成图片的url
* @since 6.0.0
*/
String imagesEdits(String prompt, File image, File mask);
/**
* 图片编辑 该方法仅支持 DALL·E 2 model
*
* @param prompt 题词
* @param image 需要编辑的图像必须是 PNG 格式
* @return 包含生成图片的url
* @since 6.0.0
*/
default String imagesEdits(String prompt, File image) {
return imagesEdits(prompt, image, null);
}
/**
* 图片变形 该方法仅支持 DALL·E 2 model
*
* @param image 需要变形的图像必须是 PNG 格式
* @return 包含生成图片的url
* @since 6.0.0
*/
String imagesVariations(File image);
/**
* TTS文本转语音 请设置config中model为支持TTS功能的模型 TTS系列
*
* @param input 需要转成语音的文本
* @param voice AI的音色
* @return 返回的音频mp3文件流
* @since 6.0.0
*/
InputStream textToSpeech(String input, OpenaiCommon.OpenaiSpeech voice);
/**
* TTS文本转语音 请设置config中model为支持TTS功能的模型 TTS系列
*
* @param input 需要转成语音的文本
* @return 返回的音频mp3文件流
* @since 6.0.0
*/
default InputStream textToSpeech(String input) {
return textToSpeech(input, OpenaiCommon.OpenaiSpeech.ALLOY);
}
/**
* STT音频转文本 请设置config中model为支持STT功能的模型 whisper
*
* @param file 需要转成文本的音频文件
* @return 返回的文本内容
* @since 6.0.0
*/
String speechToText(File file);
/**
* 文本向量化 请设置config中model为支持文本向量化功能的模型 text-embedding系列
*
* @param input 需要向量化的内容
* @return 处理后的向量信息
* @since 6.0.0
*/
String embeddingText(String input);
/**
* 检查文本或图像是否具有潜在的危害性
* 仅支持omni-moderation-latest和text-moderation-latest模型
*
* @param text 需要检查的文本
* @param imgUrl 需要检查的图片地址
* @return AI返回结果
* @since 6.0.0
*/
String moderations(String text, String imgUrl);
/**
* 检查文本是否具有潜在的危害性
* 仅支持omni-moderation-latest和text-moderation-latest模型
*
* @param text 需要检查的文本
* @return AI返回结果
* @since 6.0.0
*/
default String moderations(String text) {
return moderations(text, null);
}
/**
* 推理chat
* 支持o3-mini和o1
*
* @param prompt 对话题词
* @param reasoningEffort 推理程度
* @return AI回答
* @since 6.0.0
*/
String chatReasoning(String prompt, String reasoningEffort);
/**
* 推理chat
* 支持o3-mini和o1
*
* @param prompt 对话题词
* @return AI回答
* @since 6.0.0
*/
default String chatReasoning(String prompt) {
return chatReasoning(prompt, OpenaiCommon.OpenaiReasoning.MEDIUM.getEffort());
}
/**
* 推理chat
* 支持o3-mini和o1
*
* @param messages 消息列表
* @param reasoningEffort 推理程度
* @return AI回答
* @since 6.0.0
*/
String chatReasoning(List<Message> messages, String reasoningEffort);
/**
* 推理chat
* 支持o3-mini和o1
*
* @param messages 消息列表
* @return AI回答
* @since 6.0.0
*/
default String chatReasoning(List<Message> messages) {
return chatReasoning(messages, OpenaiCommon.OpenaiReasoning.MEDIUM.getEffort());
}
}

View File

@ -0,0 +1,292 @@
package org.dromara.hutool.ai.model.openai;
import org.dromara.hutool.ai.core.AIConfig;
import org.dromara.hutool.ai.core.BaseAIService;
import org.dromara.hutool.ai.core.Message;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.http.client.Response;
import org.dromara.hutool.json.JSONUtil;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* openai服务AI具体功能的实现
*
* @author elichow
* @since 6.0.0
*/
public class OpenaiServiceImpl extends BaseAIService implements OpenaiService {
//对话
private final String CHAT_ENDPOINT = "/chat/completions";
//文生图
private final String IMAGES_GENERATIONS = "/images/generations";
//图片编辑
private final String IMAGES_EDITS = "/images/edits";
//图片变形
private final String IMAGES_VARIATIONS = "/images/variations";
//文本转语音
private final String TTS = "/audio/speech";
//语音转文本
private final String STT = "/audio/transcriptions";
//文本向量化
private final String EMBEDDINGS = "/embeddings";
//检查文本或图片
private final String MODERATIONS = "/moderations";
public OpenaiServiceImpl(AIConfig config) {
//初始化Openai客户端
super(config);
}
@Override
public String chat(String prompt) {
// 定义消息结构
List<Message> messages = new ArrayList<>();
messages.add(new Message("system", "You are a helpful assistant"));
messages.add(new Message("user", prompt));
return chat(messages);
}
@Override
public String chat(List<Message> messages) {
String paramJson = buildChatRequestBody(messages);
Response response = sendPost(CHAT_ENDPOINT, paramJson);
return response.bodyStr();
}
@Override
public String chatVision(String prompt, List<String> images, String detail) {
String paramJson = buildChatVisionRequestBody(prompt, images, detail);
Response response = sendPost(CHAT_ENDPOINT, paramJson);
return response.bodyStr();
}
@Override
public String imagesGenerations(String prompt) {
String paramJson = buildImagesGenerationsRequestBody(prompt);
Response response = sendPost(IMAGES_GENERATIONS, paramJson);
return response.bodyStr();
}
@Override
public String imagesEdits(String prompt, File image, File mask) {
Map<String, Object> paramMap = buildImagesEditsRequestBody(prompt, image, mask);
Response response = sendFormData(IMAGES_EDITS, paramMap);
return response.bodyStr();
}
@Override
public String imagesVariations(File image) {
Map<String, Object> paramMap = buildImagesVariationsRequestBody(image);
Response response = sendFormData(IMAGES_VARIATIONS, paramMap);
return response.bodyStr();
}
@Override
public InputStream textToSpeech(String input, OpenaiCommon.OpenaiSpeech voice) {
String paramJson = buildTTSRequestBody(input, voice.getVoice());
Response response = sendPost(TTS, paramJson);
return response.bodyStream();
}
@Override
public String speechToText(File file) {
Map<String, Object> paramMap = buildSTTRequestBody(file);
Response response = sendFormData(STT, paramMap);
return response.bodyStr();
}
@Override
public String embeddingText(String input) {
String paramJson = buildEmbeddingTextRequestBody(input);
Response response = sendPost(EMBEDDINGS, paramJson);
return response.bodyStr();
}
@Override
public String moderations(String text, String imgUrl) {
String paramJson = buileModerationsRequestBody(text, imgUrl);
Response response = sendPost(MODERATIONS, paramJson);
return response.bodyStr();
}
@Override
public String chatReasoning(String prompt, String reasoningEffort) {
// 定义消息结构
List<Message> messages = new ArrayList<>();
messages.add(new Message("system", "You are a helpful assistant"));
messages.add(new Message("user", prompt));
return chat(messages);
}
@Override
public String chatReasoning(List<Message> messages, String reasoningEffort) {
String paramJson = buildChatReasoningRequestBody(messages, reasoningEffort);
Response response = sendPost(CHAT_ENDPOINT, paramJson);
return response.bodyStr();
}
// 构建chat请求体
private String buildChatRequestBody(List<Message> messages) {
//使用JSON工具
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("messages", messages);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
//构建chatVision请求体
private String buildChatVisionRequestBody(String prompt, List<String> images, String detail) {
// 定义消息结构
List<Message> messages = new ArrayList<>();
List<Object> content = new ArrayList<>();
Map<String, String> contentMap = new HashMap<>();
contentMap.put("type", "text");
contentMap.put("text", prompt);
content.add(contentMap);
for (String img : images) {
HashMap<String, Object> imgUrlMap = new HashMap<>();
imgUrlMap.put("type", "image_url");
HashMap<String, String> urlMap = new HashMap<>();
urlMap.put("url", img);
urlMap.put("detail", detail);
imgUrlMap.put("image_url", urlMap);
content.add(imgUrlMap);
}
messages.add(new Message("user", content));
//使用JSON工具
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("messages", messages);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
//构建文生图请求体
private String buildImagesGenerationsRequestBody(String prompt) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("prompt", prompt);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
//构建图片编辑请求体
private Map<String, Object> buildImagesEditsRequestBody(String prompt, File image, File mask) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("prompt", prompt);
paramMap.put("image", image);
if (mask != null) {
paramMap.put("mask", mask);
}
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return paramMap;
}
//构建图片变形请求体
private Map<String, Object> buildImagesVariationsRequestBody(File image) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("image", image);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return paramMap;
}
//构建TTS请求体
private String buildTTSRequestBody(String input, String voice) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("input", input);
paramMap.put("voice", voice);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
//构建STT请求体
private Map<String, Object> buildSTTRequestBody(File file) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("file", file);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return paramMap;
}
//构建文本向量化请求体
private String buildEmbeddingTextRequestBody(String input) {
//使用JSON工具
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("input", input);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
//构建检查图片或文字请求体
private String buileModerationsRequestBody(String text, String imgUrl) {
//使用JSON工具
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
List<Object> input = new ArrayList<>();
//添加文本参数
if (!StrUtil.isBlank(text)) {
Map<String, String> textMap = new HashMap<>();
textMap.put("type", "text");
textMap.put("text", text);
input.add(textMap);
}
//添加图片参数
if (!StrUtil.isBlank(imgUrl)) {
HashMap<String, Object> imgUrlMap = new HashMap<>();
imgUrlMap.put("type", "image_url");
HashMap<String, String> urlMap = new HashMap<>();
urlMap.put("url", imgUrl);
imgUrlMap.put("image_url", urlMap);
input.add(imgUrlMap);
}
paramMap.put("input", input);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
//构建推理请求体
private String buildChatReasoningRequestBody(List<Message> messages, String reasoningEffort) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("model", config.getModel());
paramMap.put("messages", messages);
paramMap.put("reasoning_effort", reasoningEffort);
//合并其他参数
paramMap.putAll(config.getAdditionalConfigMap());
return JSONUtil.toJsonStr(paramMap);
}
}

View File

@ -0,0 +1,4 @@
org.dromara.hutool.ai.model.deepseek.DeepSeekConfig
org.dromara.hutool.ai.model.openai.OpenaiConfig
org.dromara.hutool.ai.model.doubao.DoubaoConfig
org.dromara.hutool.ai.model.grok.GrokConfig

View File

@ -0,0 +1,4 @@
org.dromara.hutool.ai.model.deepseek.DeepSeekProvider
org.dromara.hutool.ai.model.openai.OpenaiProvider
org.dromara.hutool.ai.model.doubao.DoubaoProvider
org.dromara.hutool.ai.model.grok.GrokProvider

View File

@ -0,0 +1,25 @@
package org.dromara.hutool.ai;
import org.dromara.hutool.ai.core.AIConfigBuilder;
import org.dromara.hutool.ai.core.AIService;
import org.dromara.hutool.ai.model.deepseek.DeepSeekService;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class AIServiceFactoryTest {
String key = "your key";
@Test
void getAIService() {
AIService aiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DEEPSEEK.getValue()).setApiKey(key).build());
assertNotNull(aiService);
}
@Test
void testGetAIService() {
DeepSeekService deepSeekService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DEEPSEEK.getValue()).setApiKey(key).build(), DeepSeekService.class);
assertNotNull(deepSeekService);
}
}

View File

@ -0,0 +1,71 @@
package org.dromara.hutool.ai;
import org.dromara.hutool.ai.core.AIConfigBuilder;
import org.dromara.hutool.ai.core.AIService;
import org.dromara.hutool.ai.core.Message;
import org.dromara.hutool.ai.model.deepseek.DeepSeekService;
import org.dromara.hutool.ai.model.doubao.DoubaoService;
import org.dromara.hutool.ai.model.grok.GrokService;
import org.dromara.hutool.ai.model.openai.OpenaiService;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class AIUtilTest {
String key = "your key";
@Test
void getAIService() {
DeepSeekService deepSeekService = AIUtil.getAIService(new AIConfigBuilder(ModelName.DEEPSEEK.getValue()).setApiKey(key).build(), DeepSeekService.class);
assertNotNull(deepSeekService);
}
@Test
void testGetAIService() {
AIService aiService = AIUtil.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue()).setApiKey(key).build());
assertNotNull(aiService);
}
@Test
void getDeepSeekService() {
DeepSeekService deepSeekService = AIUtil.getDeepSeekService(new AIConfigBuilder(ModelName.DEEPSEEK.getValue()).setApiKey(key).build());
assertNotNull(deepSeekService);
}
@Test
void getDoubaoService() {
DoubaoService doubaoService = AIUtil.getDoubaoService(new AIConfigBuilder(ModelName.DOUBAO.getValue()).setApiKey(key).build());
assertNotNull(doubaoService);
}
@Test
void getGrokService() {
GrokService grokService = AIUtil.getGrokService(new AIConfigBuilder(ModelName.GROK.getValue()).setApiKey(key).build());
assertNotNull(grokService);
}
@Test
void getOpenAIService() {
OpenaiService openAIService = AIUtil.getOpenAIService(new AIConfigBuilder(ModelName.OPENAI.getValue()).setApiKey(key).build());
assertNotNull(openAIService);
}
@Test
void chat() {
String chat = AIUtil.chat(new AIConfigBuilder(ModelName.DEEPSEEK.getValue()).setApiKey(key).build(), "写一首赞美我的诗");
assertNotNull(chat);
}
@Test
void testChat() {
List<Message> messages = new ArrayList<>();
messages.add(new Message("system","你是财神爷,只会说“我是财神”"));
messages.add(new Message("user","你是谁啊?"));
String chat = AIUtil.chat(new AIConfigBuilder(ModelName.DEEPSEEK.getValue()).setApiKey(key).build(), messages);
System.out.println(chat);
}
}

View File

@ -0,0 +1,49 @@
package org.dromara.hutool.ai.model.deepseek;
import org.dromara.hutool.ai.AIServiceFactory;
import org.dromara.hutool.ai.ModelName;
import org.dromara.hutool.ai.core.AIConfigBuilder;
import org.dromara.hutool.ai.core.Message;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
class DeepSeekServiceTest {
String key = "your key";
DeepSeekService deepSeekService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DEEPSEEK.getValue()).setApiKey(key).build(),DeepSeekService.class);
@Test
void chat(){
String chat = deepSeekService.chat("写一个疯狂星期四广告词");
System.out.println(chat);
}
@Test
void testChat(){
List<Message> messages = new ArrayList<>();
messages.add(new Message("system","你是个抽象大师,会说很抽象的话,最擅长说抽象的笑话"));
messages.add(new Message("user","给我说一个笑话"));
String chat = deepSeekService.chat(messages);
System.out.println(chat);
}
@Test
void beta() {
String beta = deepSeekService.beta("写一个疯狂星期四广告词");
System.out.println(beta);
}
@Test
void models() {
String models = deepSeekService.models();
System.out.println(models);
}
@Test
void balance() {
String balance = deepSeekService.balance();
System.out.println(balance);
}
}

View File

@ -0,0 +1,163 @@
package org.dromara.hutool.ai.model.doubao;
import org.dromara.hutool.ai.AIServiceFactory;
import org.dromara.hutool.ai.ModelName;
import org.dromara.hutool.ai.Models;
import org.dromara.hutool.ai.core.AIConfigBuilder;
import org.dromara.hutool.ai.core.Message;
import org.dromara.hutool.swing.img.ImgUtil;
import org.junit.jupiter.api.Test;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class DoubaoServiceTest {
String key = "your key";
DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue()).setModel(Models.Doubao.DOUBAO_1_5_LITE_32K.getModel()).setApiKey(key).build(), DoubaoService.class);
@Test
void chat(){
String chat = doubaoService.chat("写一个疯狂星期四广告词");
System.out.println(chat);
}
@Test
void testChat(){
List<Message> messages = new ArrayList<>();
messages.add(new Message("system","你是个抽象大师,会说很抽象的话,最擅长说抽象的笑话"));
messages.add(new Message("user","给我说一个笑话"));
String chat = doubaoService.chat(messages);
System.out.println(chat);
}
@Test
void chatVision() {
DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
.setApiKey(key).setModel(Models.Doubao.DOUBAO_1_5_VISION_PRO_32K.getModel()).build(), DoubaoService.class);
String base64 = ImgUtil.toBase64DataUri(Toolkit.getDefaultToolkit().createImage("your imageUrl"), "png");
String chatVision = doubaoService.chatVision("图片上有些什么?", Arrays.asList(base64));
System.out.println(chatVision);
}
@Test
void testChatVision() {
DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
.setApiKey(key).setModel(Models.Doubao.DOUBAO_1_5_VISION_PRO_32K.getModel()).build(), DoubaoService.class);
String chatVision = doubaoService.chatVision("图片上有些什么?", Arrays.asList("https://img2.baidu.com/it/u=862000265,4064861820&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1544"),DoubaoCommon.DoubaoVision.HIGH.getDetail());
System.out.println(chatVision);
}
@Test
void videoTasks() {
DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
.setApiKey(key).setModel("your Endpoint ID").build(), DoubaoService.class);
String videoTasks = doubaoService.videoTasks("生成一段动画视频,主角是大耳朵图图,一个活泼可爱的小男孩。视频中图图在公园里玩耍," +
"画面采用明亮温暖的卡通风格,色彩鲜艳,动作流畅。背景音乐轻快活泼,带有冒险感,音效包括鸟叫声、欢笑声和山洞回声。", "https://img2.baidu.com/it/u=862000265,4064861820&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1544");
System.out.println(videoTasks);//cgt-20250306170051-6r9gk
}
@Test
void getVideoTasksInfo() {
//cgt-20250306170051-6r9gk
DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
.setApiKey(key).build(), DoubaoService.class);
String videoTasksInfo = doubaoService.getVideoTasksInfo("cgt-20250306170051-6r9gk");
System.out.println(videoTasksInfo);
}
@Test
void embeddingText() {
DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
.setApiKey(key).setModel(Models.Doubao.DOUBAO_EMBEDDING_TEXT_240715.getModel()).build(), DoubaoService.class);
String embeddingText = doubaoService.embeddingText(new String[]{"阿斯顿", "马丁"});
System.out.println(embeddingText);
}
@Test
void embeddingVision() {
DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
.setApiKey(key).setModel(Models.Doubao.DOUBAO_EMBEDDING_VISION.getModel()).build(), DoubaoService.class);
String embeddingVision = doubaoService.embeddingVision("天空好难", "https://img2.baidu.com/it/u=862000265,4064861820&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1544");
System.out.println(embeddingVision);
}
@Test
void botsChat() {
DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
.setApiKey(key).setModel("your bots id").build(), DoubaoService.class);
ArrayList<Message> messages = new ArrayList<>();
messages.add(new Message("system","你是什么都可以"));
messages.add(new Message("user","你想做些什么"));
String botsChat = doubaoService.botsChat(messages);
System.out.println(botsChat);
}
@Test
void tokenization() {
String tokenization = doubaoService.tokenization(new String[]{"阿斯顿", "马丁"});
System.out.println(tokenization);
}
@Test
void batchChat() {
DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
.setApiKey(key).setModel("your Endpoint ID").build(), DoubaoService.class);
String batchChat = doubaoService.batchChat("写首歌词");
System.out.println(batchChat);
}
@Test
void testBatchChat() {
DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
.setApiKey(key).setModel("your Endpoint ID").build(), DoubaoService.class);
List<Message> messages = new ArrayList<>();
messages.add(new Message("system","你是个抽象大师"));
messages.add(new Message("user","写一个KFC的抽象广告"));
String batchChat = doubaoService.batchChat(messages);
System.out.println(batchChat);
}
@Test
void createContext() {
DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
.setApiKey(key).setModel("your Endpoint ID").build(), DoubaoService.class);
List<Message> messages = new ArrayList<>();
messages.add(new Message("system","你是个抽象大师,你真的很抽象"));
String context = doubaoService.createContext(messages);//ctx-20250307092153-cvslm
System.out.println(context);
}
@Test
void testCreateContext() {
DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
.setApiKey(key).setModel("your Endpoint ID").build(), DoubaoService.class);
List<Message> messages = new ArrayList<>();
messages.add(new Message("system","你是个抽象大师,你真的很抽象"));
String context = doubaoService.createContext(messages,DoubaoCommon.DoubaoContext.COMMON_PREFIX.getMode());
System.out.println(context);//ctx-20250307092153-cvslm
}
@Test
void chatContext() {
//ctx-20250307092153-cvslm
DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
.setApiKey(key).setModel("eyour Endpoint ID").build(), DoubaoService.class);
String chatContext = doubaoService.chatContext("你是谁?", "ctx-20250307092153-cvslm");
System.out.println(chatContext);
}
@Test
void testChatContext() {
DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
.setApiKey(key).setModel("your Endpoint ID").build(), DoubaoService.class);
List<Message> messages = new ArrayList<>();
messages.add(new Message("user","你怎么看待意大利面拌水泥?"));
String chatContext = doubaoService.chatContext(messages, "ctx-20250307092153-cvslm");
System.out.println(chatContext);
}
}

View File

@ -0,0 +1,95 @@
package org.dromara.hutool.ai.model.grok;
import org.dromara.hutool.ai.AIServiceFactory;
import org.dromara.hutool.ai.ModelName;
import org.dromara.hutool.ai.Models;
import org.dromara.hutool.ai.core.AIConfigBuilder;
import org.dromara.hutool.ai.core.Message;
import org.dromara.hutool.swing.img.ImgUtil;
import org.junit.jupiter.api.Test;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class GrokServiceTest {
String key = "your key";
GrokService grokService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.GROK.getValue()).setApiKey(key).build(), GrokService.class);
@Test
void chat(){
String chat = grokService.chat("写一个疯狂星期四广告词");
System.out.println(chat);
}
@Test
void testChat(){
List<Message> messages = new ArrayList<>();
messages.add(new Message("system","你是个抽象大师,会说很抽象的话,最擅长说抽象的笑话"));
messages.add(new Message("user","给我说一个笑话"));
String chat = grokService.chat(messages);
System.out.println(chat);
}
@Test
void message() {
String message = grokService.message("给我一个KFC的广告词", 4096);
System.out.println(message);
}
@Test
void chatVision() {
GrokService grokService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.GROK.getValue()).setModel(Models.Grok.GROK_2_VISION_1212.getModel()).setApiKey(key).build(), GrokService.class);
String base64 = ImgUtil.toBase64DataUri(Toolkit.getDefaultToolkit().createImage("your imageUrl"), "png");
String chatVision = grokService.chatVision("图片上有些什么?", Arrays.asList(base64));
System.out.println(chatVision);
}
@Test
void testChatVision() {
GrokService grokService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.GROK.getValue()).setModel(Models.Grok.GROK_2_VISION_1212.getModel()).setApiKey(key).build(), GrokService.class);
String chatVision = grokService.chatVision("图片上有些什么?", Arrays.asList("https://img2.baidu.com/it/u=862000265,4064861820&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1544"));
System.out.println(chatVision);
}
@Test
void models() {
String models = grokService.models();
assertNotNull(models);
}
@Test
void getModel() {
String model = grokService.getModel("");
assertNotNull(model);
}
@Test
void languageModels() {
String languageModels = grokService.languageModels();
assertNotNull(languageModels);
}
@Test
void getLanguageModel() {
String language = grokService.getLanguageModel("");
assertNotNull(language);
}
@Test
void tokenizeText() {
String tokenizeText = grokService.tokenizeText(key);
assertNotNull(tokenizeText);
}
@Test
void deferredCompletion() {
String deferred = grokService.deferredCompletion(key);
assertNotNull(deferred);
}
}

View File

@ -0,0 +1,137 @@
package org.dromara.hutool.ai.model.openai;
import org.dromara.hutool.ai.AIServiceFactory;
import org.dromara.hutool.ai.ModelName;
import org.dromara.hutool.ai.Models;
import org.dromara.hutool.ai.core.AIConfigBuilder;
import org.dromara.hutool.ai.core.Message;
import org.dromara.hutool.core.io.file.FileUtil;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class OpenaiServiceTest {
String key = "your key";
OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue()).setApiKey(key).build(), OpenaiService.class);
@Test
void chat(){
String chat = openaiService.chat("写一个疯狂星期四广告词");
System.out.println(chat);
}
@Test
void testChat(){
List<Message> messages = new ArrayList<>();
messages.add(new Message("system","你是个抽象大师,会说很抽象的话,最擅长说抽象的笑话"));
messages.add(new Message("user","给我说一个笑话"));
String chat = openaiService.chat(messages);
System.out.println(chat);
}
@Test
void chatVision() {
OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
.setApiKey(key).setModel(Models.Openai.GPT_4O_MINI.getModel()).build(), OpenaiService.class);
String chatVision = openaiService.chatVision("图片上有些什么?", Arrays.asList("https://img2.baidu.com/it/u=862000265,4064861820&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1544","https://img2.baidu.com/it/u=1682510685,1244554634&fm=253&fmt=auto&app=138&f=JPEG?w=803&h=800"));
System.out.println(chatVision);
}
@Test
void imagesGenerations() {
OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
.setApiKey(key).setModel(Models.Openai.DALL_E_3.getModel()).build(), OpenaiService.class);
String imagesGenerations = openaiService.imagesGenerations("一位年轻的宇航员站在未来感十足的太空站内,透过巨大的弧形落地窗凝望浩瀚宇宙。窗外,璀璨的星河与五彩斑斓的星云交织,远处隐约可见未知星球的轮廓,仿佛在召唤着探索的脚步。宇航服上的呼吸灯与透明显示屏上的星图交相辉映,象征着人类科技与宇宙奥秘的碰撞。画面深邃而神秘,充满对未知的渴望与无限可能的想象。");
System.out.println(imagesGenerations);
//https://oaidalleapiprodscus.blob.core.windows.net/private/org-l99H6T0zCZejctB2TqdYrXFB/user-LilDVU1V8cUxJYwVAGRkUwYd/img-yA9kNatHnBiUHU5lZGim1hP2.png?st=2025-03-07T01%3A04%3A18Z&se=2025-03-07T03%3A04%3A18Z&sp=r&sv=2024-08-04&sr=b&rscd=inline&rsct=image/png&skoid=d505667d-d6c1-4a0a-bac7-5c84a87759f8&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2025-03-06T15%3A04%3A42Z&ske=2025-03-07T15%3A04%3A42Z&sks=b&skv=2024-08-04&sig=rjcRzC5U7Y3pEDZ4ME0CiviAPdIpoGO2rRTXw3m8rHw%3D
}
@Test
void imagesEdits() {
OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
.setApiKey(key).setModel(Models.Openai.DALL_E_2.getModel()).build(), OpenaiService.class);
File file = FileUtil.file("your imgUrl");
String imagesEdits = openaiService.imagesEdits("茂密的森林中,有一只九色鹿若隐若现",file);
System.out.println(imagesEdits);
}
@Test
void imagesVariations() {
OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
.setApiKey(key).setModel(Models.Openai.DALL_E_2.getModel()).build(), OpenaiService.class);
File file = FileUtil.file("your imgUrl");
String imagesVariations = openaiService.imagesVariations(file);
System.out.println(imagesVariations);
}
@Test
void textToSpeech() {
OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
.setApiKey(key).setModel(Models.Openai.TTS_1_HD.getModel()).build(), OpenaiService.class);
InputStream inputStream = openaiService.textToSpeech("万里山河一夜白,\n" +
"千峰尽染玉龙哀。\n" +
"长风卷起琼花碎,\n" +
"直上九霄揽月来。", OpenaiCommon.OpenaiSpeech.NOVA);
String filePath = "your filePath";
Path path = Paths.get(filePath);
try (FileOutputStream outputStream = new FileOutputStream(filePath)) {
Files.createDirectories(path.getParent());
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test
void speechToText() {
OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
.setApiKey(key).setModel(Models.Openai.WHISPER_1.getModel()).build(), OpenaiService.class);
File file = FileUtil.file("your filePath");
String speechToText = openaiService.speechToText(file);
System.out.println(speechToText);
}
@Test
void embeddingText() {
OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
.setApiKey(key).setModel(Models.Openai.TEXT_EMBEDDING_3_SMALL.getModel()).build(), OpenaiService.class);
String embeddingText = openaiService.embeddingText("萬里山河一夜白,千峰盡染玉龍哀,長風捲起瓊花碎,直上九霄闌月來");
System.out.println(embeddingText);
}
@Test
void moderations() {
OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
.setApiKey(key).setModel(Models.Openai.OMNI_MODERATION_LATEST.getModel()).build(), OpenaiService.class);
String moderations = openaiService.moderations("你要杀人", "https://img2.baidu.com/it/u=862000265,4064861820&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1544");
System.out.println(moderations);
}
@Test
void chatReasoning() {
OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
.setApiKey(key).setModel(Models.Openai.O3_MINI.getModel()).build(), OpenaiService.class);
List<Message> messages = new ArrayList<>();
messages.add(new Message("system","你是现代抽象家"));
messages.add(new Message("user","给我一个KFC疯狂星期四的文案"));
String chatReasoning = openaiService.chatReasoning(messages, OpenaiCommon.OpenaiReasoning.HIGH.getEffort());
System.out.println(chatReasoning);
}
}

View File

@ -96,6 +96,11 @@
<artifactId>hutool-swing</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>hutool-ai</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
<build>

View File

@ -46,6 +46,7 @@
<module>hutool-poi</module>
<module>hutool-socket</module>
<module>hutool-swing</module>
<module>hutool-ai</module>
</modules>
<properties>