mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
新增hutool-ai模块
This commit is contained in:
43
README-EN.md
43
README-EN.md
@@ -86,27 +86,28 @@ Hutool exists to reduce code search costs and avoid bugs caused by imperfect cod
|
||||
## 🛠️Module
|
||||
A Java-based tool class for files, streams, encryption and decryption, transcoding, regular, thread, XML and other JDK methods for encapsulation,composing various Util tool classes, as well as providing the following modules:
|
||||
|
||||
| module | description |
|
||||
| -------------------|-------------------------------------------------------------------------------------------------------------------------|
|
||||
| hutool-aop | JDK dynamic proxy encapsulation to provide non-IOC faceting support |
|
||||
| hutool-bloomFilter | Bloom filtering to provide some Hash algorithm Bloom filtering |
|
||||
| hutool-cache | Simple cache |
|
||||
| hutool-core | Core, including Bean operations, dates, various Utils, etc. |
|
||||
| hutool-cron | Task scheduling with Cron expressions |
|
||||
| hutool-crypto | Provides symmetric, asymmetric and digest algorithm encapsulation |
|
||||
| hutool-db | Db operations based on ActiveRecord thinking. |
|
||||
| hutool-dfa | DFA models, such as multi-keyword lookups |
|
||||
| hutool-extra | Extension modules, third-party wrappers (template engine, mail, servlet, QR code, Emoji, FTP, word splitting, etc.) |
|
||||
| hutool-http | Http client |
|
||||
| hutool-log | Log (facade) |
|
||||
| hutool-script | Script execution encapsulation, e.g. Javascript |
|
||||
| hutool-setting | Stronger Setting Profile tools and Properties tools |
|
||||
| hutool-system | System parameter tools (JVM information, etc.) |
|
||||
| hutool-json | JSON |
|
||||
| hutool-captcha | Image Captcha |
|
||||
| hutool-poi | Tools for working with Excel and Word in POI |
|
||||
| hutool-socket | Java-based tool classes for NIO and AIO sockets |
|
||||
| hutool-jwt | JSON Web Token (JWT) implement |
|
||||
| module | description |
|
||||
|--------------------|---------------------------------------------------------------------------------------------------------------------|
|
||||
| hutool-aop | JDK dynamic proxy encapsulation to provide non-IOC faceting support |
|
||||
| hutool-bloomFilter | Bloom filtering to provide some Hash algorithm Bloom filtering |
|
||||
| hutool-cache | Simple cache |
|
||||
| hutool-core | Core, including Bean operations, dates, various Utils, etc. |
|
||||
| hutool-cron | Task scheduling with Cron expressions |
|
||||
| hutool-crypto | Provides symmetric, asymmetric and digest algorithm encapsulation |
|
||||
| hutool-db | Db operations based on ActiveRecord thinking. |
|
||||
| hutool-dfa | DFA models, such as multi-keyword lookups |
|
||||
| hutool-extra | Extension modules, third-party wrappers (template engine, mail, servlet, QR code, Emoji, FTP, word splitting, etc.) |
|
||||
| hutool-http | Http client |
|
||||
| hutool-log | Log (facade) |
|
||||
| hutool-script | Script execution encapsulation, e.g. Javascript |
|
||||
| hutool-setting | Stronger Setting Profile tools and Properties tools |
|
||||
| hutool-system | System parameter tools (JVM information, etc.) |
|
||||
| hutool-json | JSON |
|
||||
| hutool-captcha | Image Captcha |
|
||||
| hutool-poi | Tools for working with Excel and Word in POI |
|
||||
| hutool-socket | Java-based tool classes for NIO and AIO sockets |
|
||||
| hutool-jwt | JSON Web Token (JWT) implement |
|
||||
| hutool-ai | AI implement |
|
||||
|
||||
Each module can be introduced individually, or all modules can be introduced by introducing `hutool-all` as required.
|
||||
|
||||
|
43
README.md
43
README.md
@@ -74,27 +74,28 @@ Hutool = Hu + tool,是原公司项目底层代码剥离后的开源库,“Hu
|
||||
## 🛠️包含组件
|
||||
一个Java基础工具类,对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装,组成各种Util工具类,同时提供以下组件:
|
||||
|
||||
| 模块 | 介绍 |
|
||||
| -------------------|---------------------------------------------------------------------------------- |
|
||||
| hutool-aop | JDK动态代理封装,提供非IOC下的切面支持 |
|
||||
| hutool-bloomFilter | 布隆过滤,提供一些Hash算法的布隆过滤 |
|
||||
| hutool-cache | 简单缓存实现 |
|
||||
| hutool-core | 核心,包括Bean操作、日期、各种Util等 |
|
||||
| hutool-cron | 定时任务模块,提供类Crontab表达式的定时任务 |
|
||||
| hutool-crypto | 加密解密模块,提供对称、非对称和摘要算法封装 |
|
||||
| hutool-db | JDBC封装后的数据操作,基于ActiveRecord思想 |
|
||||
| hutool-dfa | 基于DFA模型的多关键字查找 |
|
||||
| hutool-extra | 扩展模块,对第三方封装(模板引擎、邮件、Servlet、二维码、Emoji、FTP、分词等) |
|
||||
| hutool-http | 基于HttpUrlConnection的Http客户端封装 |
|
||||
| hutool-log | 自动识别日志实现的日志门面 |
|
||||
| hutool-script | 脚本执行封装,例如Javascript |
|
||||
| hutool-setting | 功能更强大的Setting配置文件和Properties封装 |
|
||||
| hutool-system | 系统参数调用封装(JVM信息等) |
|
||||
| hutool-json | JSON实现 |
|
||||
| hutool-captcha | 图片验证码实现 |
|
||||
| hutool-poi | 针对POI中Excel和Word的封装 |
|
||||
| hutool-socket | 基于Java的NIO和AIO的Socket封装 |
|
||||
| hutool-jwt | JSON Web Token (JWT)封装实现 |
|
||||
| 模块 | 介绍 |
|
||||
|--------------------|------------------------------------------------|
|
||||
| hutool-aop | JDK动态代理封装,提供非IOC下的切面支持 |
|
||||
| hutool-bloomFilter | 布隆过滤,提供一些Hash算法的布隆过滤 |
|
||||
| hutool-cache | 简单缓存实现 |
|
||||
| hutool-core | 核心,包括Bean操作、日期、各种Util等 |
|
||||
| hutool-cron | 定时任务模块,提供类Crontab表达式的定时任务 |
|
||||
| hutool-crypto | 加密解密模块,提供对称、非对称和摘要算法封装 |
|
||||
| hutool-db | JDBC封装后的数据操作,基于ActiveRecord思想 |
|
||||
| hutool-dfa | 基于DFA模型的多关键字查找 |
|
||||
| hutool-extra | 扩展模块,对第三方封装(模板引擎、邮件、Servlet、二维码、Emoji、FTP、分词等) |
|
||||
| hutool-http | 基于HttpUrlConnection的Http客户端封装 |
|
||||
| hutool-log | 自动识别日志实现的日志门面 |
|
||||
| hutool-script | 脚本执行封装,例如Javascript |
|
||||
| hutool-setting | 功能更强大的Setting配置文件和Properties封装 |
|
||||
| hutool-system | 系统参数调用封装(JVM信息等) |
|
||||
| hutool-json | JSON实现 |
|
||||
| hutool-captcha | 图片验证码实现 |
|
||||
| hutool-poi | 针对POI中Excel和Word的封装 |
|
||||
| hutool-socket | 基于Java的NIO和AIO的Socket封装 |
|
||||
| hutool-jwt | JSON Web Token (JWT)封装实现 |
|
||||
| hutool-ai | AI大模型封装实现 |
|
||||
|
||||
可以根据需求对每个模块单独引入,也可以通过引入`hutool-all`方式引入所有模块。
|
||||
|
||||
|
45
hutool-ai/pom.xml
Normal file
45
hutool-ai/pom.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<?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>
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.38-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-ai</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>Hutool AI大模型封装</description>
|
||||
|
||||
<properties>
|
||||
<Automatic-Module-Name>cn.hutool.ai</Automatic-Module-Name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-http</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-log</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-json</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
87
hutool-ai/src/main/java/cn/hutool/ai/AIException.java
Normal file
87
hutool-ai/src/main/java/cn/hutool/ai/AIException.java
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* 异常处理类
|
||||
*/
|
||||
public class AIException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param e 异常
|
||||
*/
|
||||
public AIException(final Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param message 消息
|
||||
*/
|
||||
public AIException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param messageTemplate 消息模板
|
||||
* @param params 参数
|
||||
*/
|
||||
public AIException(String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param message 消息
|
||||
* @param cause 被包装的子异常
|
||||
*/
|
||||
public AIException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param message 消息
|
||||
* @param cause 被包装的子异常
|
||||
* @param enableSuppression 是否启用抑制
|
||||
* @param writableStackTrace 堆栈跟踪是否应该是可写的
|
||||
*/
|
||||
public AIException(final String message, final Throwable cause, final boolean enableSuppression, final boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param throwable 被包装的子异常
|
||||
* @param messageTemplate 消息模板
|
||||
* @param params 参数
|
||||
*/
|
||||
public AIException(Throwable throwable, String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params), throwable);
|
||||
}
|
||||
}
|
80
hutool-ai/src/main/java/cn/hutool/ai/AIServiceFactory.java
Normal file
80
hutool-ai/src/main/java/cn/hutool/ai/AIServiceFactory.java
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai;
|
||||
|
||||
import cn.hutool.ai.core.AIConfig;
|
||||
import cn.hutool.ai.core.AIService;
|
||||
import cn.hutool.ai.core.AIServiceProvider;
|
||||
import cn.hutool.core.util.ServiceLoaderUtil;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 创建AIModelService的工厂类
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public class AIServiceFactory {
|
||||
|
||||
private static final Map<String, AIServiceProvider> providers = new ConcurrentHashMap<>();
|
||||
|
||||
// 加载所有 AIModelProvider 实现类
|
||||
static {
|
||||
final ServiceLoader<AIServiceProvider> loader = ServiceLoaderUtil.load(AIServiceProvider.class);
|
||||
for (final AIServiceProvider provider : loader) {
|
||||
providers.put(provider.getServiceName().toLowerCase(), provider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取AI服务
|
||||
*
|
||||
* @param config AIConfig配置
|
||||
* @return AI服务实例
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public static AIService getAIService(final AIConfig config) {
|
||||
return getAIService(config, AIService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取AI服务
|
||||
*
|
||||
* @param config AIConfig配置
|
||||
* @param clazz AI服务类
|
||||
* @return clazz对应的AI服务类实例
|
||||
* @since 5.8.38
|
||||
* @param <T> AI服务类
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends AIService> T getAIService(final AIConfig config, final Class<T> clazz) {
|
||||
final AIServiceProvider provider = providers.get(config.getModelName().toLowerCase());
|
||||
if (provider == null) {
|
||||
throw new IllegalArgumentException("Unsupported model: " + config.getModelName());
|
||||
}
|
||||
|
||||
final AIService service = provider.create(config);
|
||||
if (!clazz.isInstance(service)) {
|
||||
throw new AIException("Model service is not of type: " + clazz.getSimpleName());
|
||||
}
|
||||
|
||||
return (T) service;
|
||||
}
|
||||
}
|
129
hutool-ai/src/main/java/cn/hutool/ai/AIUtil.java
Normal file
129
hutool-ai/src/main/java/cn/hutool/ai/AIUtil.java
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai;
|
||||
|
||||
import cn.hutool.ai.core.AIConfig;
|
||||
import cn.hutool.ai.core.AIService;
|
||||
import cn.hutool.ai.core.Message;
|
||||
import cn.hutool.ai.model.deepseek.DeepSeekService;
|
||||
import cn.hutool.ai.model.doubao.DoubaoService;
|
||||
import cn.hutool.ai.model.grok.GrokService;
|
||||
import cn.hutool.ai.model.openai.OpenaiService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* AI工具类
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public class AIUtil {
|
||||
|
||||
/**
|
||||
* 获取AI模型服务,每个大模型提供的功能会不一样,可以调用此方法指定不同AI服务类,调用不同的功能
|
||||
*
|
||||
* @param config 创建的AI服务模型的配置
|
||||
* @param clazz AI模型服务类
|
||||
* @return AIModelService的实现类实例
|
||||
* @since 5.8.38
|
||||
* @param <T> AIService实现类
|
||||
*/
|
||||
public static <T extends AIService> T getAIService(final AIConfig config, final Class<T> clazz) {
|
||||
return AIServiceFactory.getAIService(config, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取AI模型服务
|
||||
*
|
||||
* @param config 创建的AI服务模型的配置
|
||||
* @return AIModelService 其中只有公共方法
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public static AIService getAIService(final AIConfig config) {
|
||||
return getAIService(config, AIService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取DeepSeek模型服务
|
||||
*
|
||||
* @param config 创建的AI服务模型的配置
|
||||
* @return DeepSeekService
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public static DeepSeekService getDeepSeekService(final AIConfig config) {
|
||||
return getAIService(config, DeepSeekService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Doubao模型服务
|
||||
*
|
||||
* @param config 创建的AI服务模型的配置
|
||||
* @return DoubaoService
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public static DoubaoService getDoubaoService(final AIConfig config) {
|
||||
return getAIService(config, DoubaoService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Grok模型服务
|
||||
*
|
||||
* @param config 创建的AI服务模型的配置
|
||||
* @return GrokService
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public static GrokService getGrokService(final AIConfig config) {
|
||||
return getAIService(config, GrokService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Openai模型服务
|
||||
*
|
||||
* @param config 创建的AI服务模型的配置
|
||||
* @return OpenAIService
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public static OpenaiService getOpenAIService(final AIConfig config) {
|
||||
return getAIService(config, OpenaiService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* AI大模型对话功能
|
||||
*
|
||||
* @param config 创建的AI服务模型的配置
|
||||
* @param prompt 需要对话的内容
|
||||
* @return AI模型返回的Response响应字符串
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public static String chat(final AIConfig config, final String prompt) {
|
||||
return getAIService(config).chat(prompt);
|
||||
}
|
||||
|
||||
/**
|
||||
* AI大模型对话功能
|
||||
*
|
||||
* @param config 创建的AI服务模型的配置
|
||||
* @param messages 由目前为止的对话组成的消息列表,可以设置role,content。详细参考官方文档
|
||||
* @return AI模型返回的Response响应字符串
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public static String chat(final AIConfig config, final List<Message> messages) {
|
||||
return getAIService(config).chat(messages);
|
||||
}
|
||||
|
||||
}
|
57
hutool-ai/src/main/java/cn/hutool/ai/ModelName.java
Normal file
57
hutool-ai/src/main/java/cn/hutool/ai/ModelName.java
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai;
|
||||
|
||||
/**
|
||||
* 模型厂商的名称(不指具体的模型)
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public enum ModelName {
|
||||
/**
|
||||
* deepSeek
|
||||
*/
|
||||
DEEPSEEK("deepSeek"),
|
||||
/**
|
||||
* openai
|
||||
*/
|
||||
OPENAI("openai"),
|
||||
/**
|
||||
* doubao
|
||||
*/
|
||||
DOUBAO("doubao"),
|
||||
/**
|
||||
* grok
|
||||
*/
|
||||
GROK("grok");
|
||||
|
||||
private final String value;
|
||||
|
||||
ModelName(final String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取值
|
||||
*
|
||||
* @return 值
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
157
hutool-ai/src/main/java/cn/hutool/ai/Models.java
Normal file
157
hutool-ai/src/main/java/cn/hutool/ai/Models.java
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai;
|
||||
|
||||
/**
|
||||
* 各模型厂商包含的model(指具体的模型)
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
113
hutool-ai/src/main/java/cn/hutool/ai/core/AIConfig.java
Normal file
113
hutool-ai/src/main/java/cn/hutool/ai/core/AIConfig.java
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.core;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* AI配置类
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public interface AIConfig {
|
||||
|
||||
/**
|
||||
* 获取模型(厂商)名称
|
||||
*
|
||||
* @return 模型(厂商)名称
|
||||
* @since 5.8.38
|
||||
*/
|
||||
default String getModelName() {
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置apiKey
|
||||
*
|
||||
* @param apiKey apiKey
|
||||
* @since 5.8.38
|
||||
*/
|
||||
void setApiKey(String apiKey);
|
||||
|
||||
/**
|
||||
* 获取apiKey
|
||||
*
|
||||
* @return apiKey
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String getApiKey();
|
||||
|
||||
/**
|
||||
* 设置apiUrl
|
||||
*
|
||||
* @param apiUrl api请求地址
|
||||
* @since 5.8.38
|
||||
*/
|
||||
void setApiUrl(String apiUrl);
|
||||
|
||||
/**
|
||||
* 获取apiUrl
|
||||
*
|
||||
* @return apiUrl
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String getApiUrl();
|
||||
|
||||
/**
|
||||
* 设置model
|
||||
*
|
||||
* @param model model
|
||||
* @since 5.8.38
|
||||
*/
|
||||
void setModel(String model);
|
||||
|
||||
/**
|
||||
* 返回model
|
||||
*
|
||||
* @return model
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String getModel();
|
||||
|
||||
/**
|
||||
* 设置动态参数
|
||||
*
|
||||
* @param key 参数字段
|
||||
* @param value 参数值
|
||||
* @since 5.8.38
|
||||
*/
|
||||
void putAdditionalConfigByKey(String key, Object value);
|
||||
|
||||
/**
|
||||
* 获取动态参数
|
||||
*
|
||||
* @param key 参数字段
|
||||
* @return 参数值
|
||||
* @since 5.8.38
|
||||
*/
|
||||
Object getAdditionalConfigByKey(String key);
|
||||
|
||||
/**
|
||||
* 获取动态参数列表
|
||||
*
|
||||
* @return 参数列表Map
|
||||
* @since 5.8.38
|
||||
*/
|
||||
Map<String, Object> getAdditionalConfigMap();
|
||||
|
||||
}
|
118
hutool-ai/src/main/java/cn/hutool/ai/core/AIConfigBuilder.java
Normal file
118
hutool-ai/src/main/java/cn/hutool/ai/core/AIConfigBuilder.java
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.core;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
/**
|
||||
* 用于AIConfig的创建,创建同时支持链式设置参数
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public class AIConfigBuilder {
|
||||
|
||||
private final AIConfig config;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param modelName 模型厂商的名称(注意不是指具体的模型)
|
||||
*/
|
||||
public AIConfigBuilder(final String modelName) {
|
||||
try {
|
||||
// 获取配置类
|
||||
final Class<? extends AIConfig> configClass = AIConfigRegistry.getConfigClass(modelName);
|
||||
if (configClass == null) {
|
||||
throw new IllegalArgumentException("Unsupported model: " + modelName);
|
||||
}
|
||||
|
||||
// 使用反射创建实例
|
||||
final Constructor<? extends AIConfig> constructor = configClass.getDeclaredConstructor();
|
||||
config = constructor.newInstance();
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException("Failed to create AIConfig instance", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置apiKey
|
||||
*
|
||||
* @param apiKey apiKey
|
||||
* @return config
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public synchronized AIConfigBuilder setApiKey(final String apiKey) {
|
||||
if (apiKey != null) {
|
||||
config.setApiKey(apiKey);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置AI模型请求API接口的地址,不设置为默认值
|
||||
*
|
||||
* @param apiUrl API接口地址
|
||||
* @return config
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public synchronized AIConfigBuilder setApiUrl(final String apiUrl) {
|
||||
if (apiUrl != null) {
|
||||
config.setApiUrl(apiUrl);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置具体的model,不设置为默认值
|
||||
*
|
||||
* @param model 具体model的名称
|
||||
* @return config
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public synchronized AIConfigBuilder setModel(final String model) {
|
||||
if (model != null) {
|
||||
config.setModel(model);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态设置Request请求体中的属性字段,每个模型功能支持的字段请参照对应的官方文档
|
||||
*
|
||||
* @param key Request中的支持的属性名
|
||||
* @param value 设置的属性值
|
||||
* @return config
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public AIConfigBuilder putAdditionalConfig(final String key, final Object value) {
|
||||
if (value != null) {
|
||||
config.putAdditionalConfigByKey(key, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回config实例
|
||||
*
|
||||
* @return config
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public AIConfig build() {
|
||||
return config;
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.core;
|
||||
|
||||
import cn.hutool.core.util.ServiceLoaderUtil;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* AIConfig实现类的加载器
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public class AIConfigRegistry {
|
||||
|
||||
private static final Map<String, Class<? extends AIConfig>> configClasses = new ConcurrentHashMap<>();
|
||||
|
||||
// 加载所有 AIConfig 实现类
|
||||
static {
|
||||
final ServiceLoader<AIConfig> loader = ServiceLoaderUtil.load(AIConfig.class);
|
||||
for (final AIConfig config : loader) {
|
||||
configClasses.put(config.getModelName().toLowerCase(), config.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模型名称获取AIConfig实现类
|
||||
*
|
||||
* @param modelName 模型名称
|
||||
* @return AIConfig实现类
|
||||
*/
|
||||
public static Class<? extends AIConfig> getConfigClass(final String modelName) {
|
||||
return configClasses.get(modelName.toLowerCase());
|
||||
}
|
||||
}
|
47
hutool-ai/src/main/java/cn/hutool/ai/core/AIService.java
Normal file
47
hutool-ai/src/main/java/cn/hutool/ai/core/AIService.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 模型公共的API功能,特有的功能在model.xx.XXService下定义
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public interface AIService {
|
||||
|
||||
/**
|
||||
* 对话
|
||||
*
|
||||
* @param prompt user题词
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String chat(String prompt);
|
||||
|
||||
/**
|
||||
* 对话
|
||||
*
|
||||
* @param messages 由目前为止的对话组成的消息列表,可以设置role,content。详细参考官方文档
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String chat(final List<Message> messages);
|
||||
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.core;
|
||||
|
||||
/**
|
||||
* 用于加载AI服务,每一个通过SPI创建的AI服务都要实现此接口
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public interface AIServiceProvider {
|
||||
|
||||
/**
|
||||
* 获取AI服务名称
|
||||
*
|
||||
* @return AI服务名称
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String getServiceName();
|
||||
|
||||
/**
|
||||
* 创建AI服务实例
|
||||
*
|
||||
* @param config AIConfig配置
|
||||
* @param <T> AIService实现类
|
||||
* @return AI服务实例
|
||||
* @since 5.8.38
|
||||
*/
|
||||
<T extends AIService> T create(final AIConfig config);
|
||||
}
|
105
hutool-ai/src/main/java/cn/hutool/ai/core/BaseAIService.java
Normal file
105
hutool-ai/src/main/java/cn/hutool/ai/core/BaseAIService.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.core;
|
||||
|
||||
import cn.hutool.ai.AIException;
|
||||
import cn.hutool.http.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 基础AIService,包含基公共参数和公共方法
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public class BaseAIService {
|
||||
|
||||
protected final AIConfig config;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
*
|
||||
* @param config AI配置
|
||||
*/
|
||||
public BaseAIService(final AIConfig config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送Get请求
|
||||
* @param endpoint 请求节点
|
||||
* @return 请求响应
|
||||
*/
|
||||
protected HttpResponse sendGet(final String endpoint) {
|
||||
//链式构建请求
|
||||
try {
|
||||
//设置超时3分钟
|
||||
return HttpRequest.get(config.getApiUrl() + endpoint)
|
||||
.header(Header.ACCEPT, "application/json")
|
||||
.header(Header.AUTHORIZATION, "Bearer " + config.getApiKey())
|
||||
.timeout(180000)
|
||||
.execute();
|
||||
} catch (final AIException e) {
|
||||
throw new AIException("Failed to send GET request: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送Post请求
|
||||
* @param endpoint 请求节点
|
||||
* @param paramJson 请求参数json
|
||||
* @return 请求响应
|
||||
*/
|
||||
protected HttpResponse sendPost(final String endpoint, final String paramJson) {
|
||||
//链式构建请求
|
||||
try {
|
||||
return HttpRequest.post(config.getApiUrl() + endpoint)
|
||||
.header(Header.CONTENT_TYPE, "application/json")
|
||||
.header(Header.ACCEPT, "application/json")
|
||||
.header(Header.AUTHORIZATION, "Bearer " + config.getApiKey())
|
||||
.body(paramJson)
|
||||
.timeout(180000)
|
||||
.execute();
|
||||
} catch (final AIException e) {
|
||||
throw new AIException("Failed to send POST request:" + e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送表单请求
|
||||
* @param endpoint 请求节点
|
||||
* @param paramMap 请求参数map
|
||||
* @return 请求响应
|
||||
*/
|
||||
protected HttpResponse sendFormData(final String endpoint, final Map<String, Object> paramMap) {
|
||||
//链式构建请求
|
||||
try {
|
||||
//设置超时3分钟
|
||||
return HttpRequest.post(config.getApiUrl() + endpoint)
|
||||
.header(Header.CONTENT_TYPE, "multipart/form-data")
|
||||
.header(Header.ACCEPT, "application/json")
|
||||
.header(Header.AUTHORIZATION, "Bearer " + config.getApiKey())
|
||||
.form(paramMap)
|
||||
.timeout(180000)
|
||||
.execute();
|
||||
} catch (final AIException e) {
|
||||
throw new AIException("Failed to send POST request:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
84
hutool-ai/src/main/java/cn/hutool/ai/core/BaseConfig.java
Normal file
84
hutool-ai/src/main/java/cn/hutool/ai/core/BaseConfig.java
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.core;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Config基础类,定义模型配置的基本属性
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
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 ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void setApiKey(final String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApiUrl(final String apiUrl) {
|
||||
this.apiUrl = apiUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getApiUrl() {
|
||||
return apiUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModel(final String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAdditionalConfigByKey(final String key, final Object value) {
|
||||
this.additionalConfig.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAdditionalConfigByKey(final String key) {
|
||||
return additionalConfig.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAdditionalConfigMap() {
|
||||
return new ConcurrentHashMap<>(additionalConfig);
|
||||
}
|
||||
|
||||
}
|
59
hutool-ai/src/main/java/cn/hutool/ai/core/Message.java
Normal file
59
hutool-ai/src/main/java/cn/hutool/ai/core/Message.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.core;
|
||||
|
||||
/**
|
||||
* 公共Message类
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public class Message {
|
||||
//角色 注意:如果设置系统消息,请放在messages列表的第一位
|
||||
private final String role;
|
||||
//内容
|
||||
private final Object content;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param role 角色
|
||||
* @param content 内容
|
||||
*/
|
||||
public Message(final String role, final Object content) {
|
||||
this.role = role;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色
|
||||
*
|
||||
* @return 角色
|
||||
*/
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取内容
|
||||
*
|
||||
* @return 内容
|
||||
*/
|
||||
public Object getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
24
hutool-ai/src/main/java/cn/hutool/ai/core/package-info.java
Normal file
24
hutool-ai/src/main/java/cn/hutool/ai/core/package-info.java
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* AI相关基础类
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.core;
|
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.deepseek;
|
||||
|
||||
/**
|
||||
* deepSeek公共类
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public class DeepSeekCommon {
|
||||
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.deepseek;
|
||||
|
||||
import cn.hutool.ai.Models;
|
||||
import cn.hutool.ai.core.BaseConfig;
|
||||
|
||||
/**
|
||||
* DeepSeek配置类,初始化API接口地址,设置默认的模型
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.deepseek;
|
||||
|
||||
import cn.hutool.ai.core.AIConfig;
|
||||
import cn.hutool.ai.core.AIServiceProvider;
|
||||
|
||||
/**
|
||||
* 创建DeepSeek服务实现类
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public class DeepSeekProvider implements AIServiceProvider {
|
||||
|
||||
@Override
|
||||
public String getServiceName() {
|
||||
return "deepSeek";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeepSeekService create(final AIConfig config) {
|
||||
return new DeepSeekServiceImpl(config);
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.deepseek;
|
||||
|
||||
import cn.hutool.ai.core.AIService;
|
||||
|
||||
/**
|
||||
* deepSeek支持的扩展接口
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public interface DeepSeekService extends AIService {
|
||||
|
||||
/**
|
||||
* 模型beta功能
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @return AI的回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String beta(String prompt);
|
||||
|
||||
/**
|
||||
* 列出所有模型列表
|
||||
*
|
||||
* @return model列表
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String models();
|
||||
|
||||
/**
|
||||
* 查询余额
|
||||
*
|
||||
* @return 余额
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String balance();
|
||||
}
|
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.deepseek;
|
||||
|
||||
import cn.hutool.ai.core.AIConfig;
|
||||
import cn.hutool.ai.core.BaseAIService;
|
||||
import cn.hutool.ai.core.Message;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* DeepSeek服务,AI具体功能的实现
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
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";
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param config AI配置
|
||||
*/
|
||||
public DeepSeekServiceImpl(final AIConfig config) {
|
||||
//初始化DeepSeek客户端
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chat(final String prompt) {
|
||||
// 定义消息结构
|
||||
final 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(final List<Message> messages) {
|
||||
final String paramJson = buildChatRequestBody(messages);
|
||||
final HttpResponse response = sendPost(CHAT_ENDPOINT, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String beta(final String prompt) {
|
||||
final String paramJson = buildBetaRequestBody(prompt);
|
||||
final HttpResponse response = sendPost(BETA_ENDPOINT, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String models() {
|
||||
final HttpResponse response = sendGet(MODELS_ENDPOINT);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String balance() {
|
||||
final HttpResponse response = sendGet(BALANCE_ENDPOINT);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
// 构建chat请求体
|
||||
private String buildChatRequestBody(final List<Message> messages) {
|
||||
//使用JSON工具
|
||||
final 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(final String prompt) {
|
||||
// 定义消息结构
|
||||
//使用JSON工具
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("prompt", prompt);
|
||||
// //合并其他参数
|
||||
paramMap.putAll(config.getAdditionalConfigMap());
|
||||
|
||||
return JSONUtil.toJsonStr(paramMap);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 对deepSeek的封装实现
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.deepseek;
|
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.doubao;
|
||||
|
||||
/**
|
||||
* doubao公共类
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
//doubao视觉参数
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.doubao;
|
||||
|
||||
import cn.hutool.ai.Models;
|
||||
import cn.hutool.ai.core.BaseConfig;
|
||||
|
||||
/**
|
||||
* Doubao配置类,初始化API接口地址,设置默认的模型
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.doubao;
|
||||
|
||||
import cn.hutool.ai.core.AIConfig;
|
||||
import cn.hutool.ai.core.AIServiceProvider;
|
||||
|
||||
/**
|
||||
* 创建Doubap服务实现类
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public class DoubaoProvider implements AIServiceProvider {
|
||||
|
||||
@Override
|
||||
public String getServiceName() {
|
||||
return "doubao";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubaoService create(final AIConfig config) {
|
||||
return new DoubaoServiceImpl(config);
|
||||
}
|
||||
}
|
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.doubao;
|
||||
|
||||
import cn.hutool.ai.core.AIService;
|
||||
import cn.hutool.ai.core.Message;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* doubao支持的扩展接口
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public interface DoubaoService extends AIService {
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 提问
|
||||
* @param images 图片列表/或者图片Base64编码图片列表(URI形式)
|
||||
* @param detail 手动设置图片的质量,取值范围high、low、auto,默认为auto
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String chatVision(String prompt, final List<String> images, String detail);
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 提问
|
||||
* @param images 传入的图片列表地址/或者图片Base64编码图片列表(URI形式)
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
default String chatVision(String prompt, final List<String> images) {
|
||||
return chatVision(prompt, images, DoubaoCommon.DoubaoVision.AUTO.getDetail());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建视频生成任务
|
||||
* 注意:调用该方法时,配置config中的model为您创建的推理接入点(Endpoint)ID。详细参考官方文档
|
||||
*
|
||||
* @param text 文本提示词
|
||||
* @param image 图片/或者图片Base64编码图片(URI形式)
|
||||
* @param videoParams 视频参数列表
|
||||
* @return 生成任务id
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String videoTasks(String text, String image, final List<DoubaoCommon.DoubaoVideo> videoParams);
|
||||
|
||||
/**
|
||||
* 创建视频生成任务
|
||||
* 注意:调用该方法时,配置config中的model为您创建的推理接入点(Endpoint)ID。详细参考官方文档
|
||||
*
|
||||
* @param text 文本提示词
|
||||
* @param image 图片/或者图片Base64编码图片(URI形式)
|
||||
* @return 生成任务id
|
||||
* @since 5.8.38
|
||||
*/
|
||||
default String videoTasks(String text, String image) {
|
||||
return videoTasks(text, image, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询视频生成任务信息
|
||||
*
|
||||
* @param taskId 通过创建生成视频任务返回的生成任务id
|
||||
* @return 生成任务信息
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String getVideoTasksInfo(String taskId);
|
||||
|
||||
/**
|
||||
* 文本向量化
|
||||
*
|
||||
* @param input 需要向量化的内容列表,支持中文、英文
|
||||
* @return 处理后的向量信息
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String embeddingText(String[] input);
|
||||
|
||||
/**
|
||||
* 图文向量化:仅支持单一文本、单张图片或文本与图片的组合输入(即一段文本 + 一张图片),暂不支持批量文本 / 图片的同时处理
|
||||
*
|
||||
* @param text 需要向量化的内容
|
||||
* @param image 需要向量化的图片地址/或者图片Base64编码图片(URI形式)
|
||||
* @return 处理后的向量信息
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String embeddingVision(String text, String image);
|
||||
|
||||
/**
|
||||
* 应用(Bot) config中model设置为您创建的应用ID
|
||||
*
|
||||
* @param messages 由对话组成的消息列表。如系统人设,背景信息等,用户自定义的信息
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String botsChat(final List<Message> messages);
|
||||
|
||||
/**
|
||||
* 分词:可以将文本转换为模型可理解的 token id,并返回文本的 tokens 数量、token id、 token 在原始文本中的偏移量等信息
|
||||
*
|
||||
* @param text 需要分词的内容列表
|
||||
* @return 分词结果
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String tokenization(String[] text);
|
||||
|
||||
/**
|
||||
* 批量推理 Chat
|
||||
* 注意:调用该方法时,配置config中的model为您创建的批量推理接入点(Endpoint)ID。详细参考官方文档
|
||||
* 该方法不支持流式
|
||||
*
|
||||
* @param prompt chat内容
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String batchChat(String prompt);
|
||||
|
||||
/**
|
||||
* 批量推理 Chat
|
||||
* 注意:调用该方法时,配置config中的model为您创建的批量推理接入点(Endpoint)ID。详细参考官方文档
|
||||
* 该方法不支持流式
|
||||
*
|
||||
* @param messages 由对话组成的消息列表。如系统人设,背景信息等,用户自定义的信息
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String batchChat(final List<Message> messages);
|
||||
|
||||
/**
|
||||
* 创建上下文缓存: 创建上下文缓存,获得缓存 id字段后,在上下文缓存对话 API中使用。
|
||||
* 注意:调用该方法时,配置config中的model为您创建的推理接入点(Endpoint)ID,
|
||||
* 推理接入点中使用的模型需要在模型管理中开启缓存功能。详细参考官方文档
|
||||
*
|
||||
* @param messages 由对话组成的消息列表。如系统人设,背景信息等,用户自定义的信息
|
||||
* @param mode 上下文缓存的类型,详细参考官方文档 默认为session
|
||||
* @return 返回的缓存id
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String createContext(final List<Message> messages, String mode);
|
||||
|
||||
/**
|
||||
* 创建上下文缓存: 创建上下文缓存,获得缓存 id字段后,在上下文缓存对话 API中使用。
|
||||
* 注意:调用该方法时,配置config中的model为您创建的推理接入点(Endpoint)ID,
|
||||
* 推理接入点中使用的模型需要在模型管理中开启缓存功能。详细参考官方文档
|
||||
*
|
||||
* @param messages 由对话组成的消息列表。如系统人设,背景信息等,用户自定义的信息
|
||||
* @return 返回的缓存id
|
||||
* @since 5.8.38
|
||||
*/
|
||||
default String createContext(final List<Message> messages) {
|
||||
return createContext(messages, DoubaoCommon.DoubaoContext.SESSION.getMode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 上下文缓存对话: 向大模型发起带上下文缓存的请求
|
||||
* 注意:配置config中的model可以为您创建的推理接入点(Endpoint)ID,也可以是支持chat的model
|
||||
*
|
||||
* @param prompt 对话的内容题词
|
||||
* @param contextId 创建上下文缓存后获取的缓存id
|
||||
* @return AI的回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String chatContext(String prompt, String contextId);
|
||||
|
||||
/**
|
||||
* 上下文缓存对话: 向大模型发起带上下文缓存的请求
|
||||
* 注意:配置config中的model可以为您创建的推理接入点(Endpoint)ID,也可以是支持chat的model
|
||||
*
|
||||
* @param messages 对话的信息 不支持最后一个元素的role设置为assistant。如使用session 缓存(mode设置为session)传入最新一轮对话的信息,无需传入历史信息
|
||||
* @param contextId 创建上下文缓存后获取的缓存id
|
||||
* @return AI的回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String chatContext(final List<Message> messages, String contextId);
|
||||
|
||||
}
|
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.doubao;
|
||||
|
||||
import cn.hutool.ai.core.AIConfig;
|
||||
import cn.hutool.ai.core.BaseAIService;
|
||||
import cn.hutool.ai.core.Message;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Doubao服务,AI具体功能的实现
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
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(final AIConfig config) {
|
||||
//初始化doubao客户端
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chat(String prompt) {
|
||||
// 定义消息结构
|
||||
final 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(final List<Message> messages) {
|
||||
String paramJson = buildChatRequestBody(messages);
|
||||
final HttpResponse response = sendPost(CHAT_ENDPOINT, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chatVision(String prompt, final List<String> images, String detail) {
|
||||
String paramJson = buildChatVisionRequestBody(prompt, images, detail);
|
||||
final HttpResponse response = sendPost(CHAT_ENDPOINT, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String videoTasks(String text, String image, final List<DoubaoCommon.DoubaoVideo> videoParams) {
|
||||
String paramJson = buildGenerationsTasksRequestBody(text, image, videoParams);
|
||||
final HttpResponse response = sendPost(CREATE_VIDEO, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVideoTasksInfo(String taskId) {
|
||||
final HttpResponse response = sendGet(CREATE_VIDEO + "/" + taskId);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String embeddingText(String[] input) {
|
||||
String paramJson = buildEmbeddingTextRequestBody(input);
|
||||
final HttpResponse response = sendPost(EMBEDDING_TEXT, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String embeddingVision(String text, String image) {
|
||||
String paramJson = buildEmbeddingVisionRequestBody(text, image);
|
||||
final HttpResponse response = sendPost(EMBEDDING_VISION, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String botsChat(final List<Message> messages) {
|
||||
String paramJson = buildBotsChatRequestBody(messages);
|
||||
final HttpResponse response = sendPost(BOTS_CHAT, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tokenization(String[] text) {
|
||||
String paramJson = buildTokenizationRequestBody(text);
|
||||
final HttpResponse response = sendPost(TOKENIZATION, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String batchChat(String prompt) {
|
||||
// 定义消息结构
|
||||
final 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(final List<Message> messages) {
|
||||
String paramJson = buildBatchChatRequestBody(messages);
|
||||
final HttpResponse response = sendPost(BATCH_CHAT, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createContext(final List<Message> messages, String mode) {
|
||||
String paramJson = buildCreateContextRequest(messages, mode);
|
||||
final HttpResponse response = sendPost(CREATE_CONTEXT, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chatContext(String prompt, String contextId) {
|
||||
// 定义消息结构
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("user", prompt));
|
||||
return chatContext(messages, contextId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chatContext(final List<Message> messages, String contextId) {
|
||||
String paramJson = buildChatContentRequestBody(messages, contextId);
|
||||
final HttpResponse response = sendPost(CHAT_CONTEXT, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
// 构建chat请求体
|
||||
private String buildChatRequestBody(final List<Message> messages) {
|
||||
//使用JSON工具
|
||||
final 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, final List<String> images, String detail) {
|
||||
// 定义消息结构
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
final List<Object> content = new ArrayList<>();
|
||||
|
||||
final 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工具
|
||||
final 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工具
|
||||
final 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工具
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("model", config.getModel());
|
||||
|
||||
final List<Object> input = new ArrayList<>();
|
||||
//添加文本参数
|
||||
if (!StrUtil.isBlank(text)) {
|
||||
final Map<String, String> textMap = new HashMap<>();
|
||||
textMap.put("type", "text");
|
||||
textMap.put("text", text);
|
||||
input.add(textMap);
|
||||
}
|
||||
//添加图片参数
|
||||
if (!StrUtil.isBlank(image)) {
|
||||
final Map<String, Object> imgUrlMap = new HashMap<>();
|
||||
imgUrlMap.put("type", "image_url");
|
||||
final Map<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(final List<Message> messages) {
|
||||
return buildChatRequestBody(messages);
|
||||
}
|
||||
|
||||
//构建分词请求体
|
||||
private String buildTokenizationRequestBody(String[] text) {
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("text", text);
|
||||
return JSONUtil.toJsonStr(paramMap);
|
||||
}
|
||||
|
||||
//构建批量推理chat请求体
|
||||
private String buildBatchChatRequestBody(final List<Message> messages) {
|
||||
return buildChatRequestBody(messages);
|
||||
}
|
||||
|
||||
//构建创建上下文缓存请求体
|
||||
private String buildCreateContextRequest(final List<Message> messages, String mode) {
|
||||
final 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(final List<Message> messages, String contextId) {
|
||||
//使用JSON工具
|
||||
final 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, final List<DoubaoCommon.DoubaoVideo> videoParams) {
|
||||
//使用JSON工具
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("model", config.getModel());
|
||||
|
||||
final List<Object> content = new ArrayList<>();
|
||||
//添加文本参数
|
||||
final 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)) {
|
||||
final Map<String, Object> imgUrlMap = new HashMap<>();
|
||||
imgUrlMap.put("type", "image_url");
|
||||
final Map<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);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 对doubao的封装实现
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.doubao;
|
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.grok;
|
||||
|
||||
/**
|
||||
* grok公共类
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.grok;
|
||||
|
||||
import cn.hutool.ai.Models;
|
||||
import cn.hutool.ai.core.BaseConfig;
|
||||
|
||||
/**
|
||||
* Grok配置类,初始化API接口地址,设置默认的模型
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.grok;
|
||||
|
||||
import cn.hutool.ai.core.AIConfig;
|
||||
import cn.hutool.ai.core.AIServiceProvider;
|
||||
|
||||
/**r
|
||||
* 创建Grok服务实现类
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public class GrokProvider implements AIServiceProvider {
|
||||
|
||||
@Override
|
||||
public String getServiceName() {
|
||||
return "grok";
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrokService create(final AIConfig config) {
|
||||
return new GrokServiceImpl(config);
|
||||
}
|
||||
}
|
115
hutool-ai/src/main/java/cn/hutool/ai/model/grok/GrokService.java
Normal file
115
hutool-ai/src/main/java/cn/hutool/ai/model/grok/GrokService.java
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.grok;
|
||||
|
||||
import cn.hutool.ai.core.AIService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* grok支持的扩展接口
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public interface GrokService extends AIService {
|
||||
|
||||
/**
|
||||
* 创建消息回复
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param maxToken 最大token
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String message(String prompt, int maxToken);
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 图片列表/或者图片Base64编码图片列表(URI形式)
|
||||
* @param detail 手动设置图片的质量,取值范围high、low、auto,默认为auto
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String chatVision(String prompt, final List<String> images, String detail);
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 传入的图片列表地址/或者图片Base64编码图片列表(URI形式)
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
default String chatVision(String prompt, final List<String> images) {
|
||||
return chatVision(prompt, images, GrokCommon.GrokVision.AUTO.getDetail());
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出所有model列表
|
||||
*
|
||||
* @return model列表
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String models();
|
||||
|
||||
/**
|
||||
* 获取模型信息
|
||||
*
|
||||
* @param modelId model ID
|
||||
* @return model信息
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String getModel(String modelId);
|
||||
|
||||
/**
|
||||
* 列出所有语言model
|
||||
*
|
||||
* @return languageModel列表
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String languageModels();
|
||||
|
||||
/**
|
||||
* 获取语言模型信息
|
||||
*
|
||||
* @param modelId model ID
|
||||
* @return model信息
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String getLanguageModel(String modelId);
|
||||
|
||||
/**
|
||||
* 分词:可以将文本转换为模型可理解的 token 信息
|
||||
*
|
||||
* @param text 需要分词的内容
|
||||
* @return 分词结果
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String tokenizeText(String text);
|
||||
|
||||
/**
|
||||
* 从延迟对话中获取结果
|
||||
*
|
||||
* @param requestId 延迟对话中的延迟请求ID
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String deferredCompletion(String requestId);
|
||||
}
|
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.grok;
|
||||
|
||||
import cn.hutool.ai.core.AIConfig;
|
||||
import cn.hutool.ai.core.BaseAIService;
|
||||
import cn.hutool.ai.core.Message;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Grok服务,AI具体功能的实现
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
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(final AIConfig config) {
|
||||
//初始化grok客户端
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chat(String prompt) {
|
||||
// 定义消息结构
|
||||
final 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(final List<Message> messages) {
|
||||
String paramJson = buildChatRequestBody(messages);
|
||||
final HttpResponse response = sendPost(CHAT_ENDPOINT, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String message(String prompt, int maxToken) {
|
||||
// 定义消息结构
|
||||
final 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);
|
||||
final HttpResponse response = sendPost(MESSAGES, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chatVision(String prompt, final List<String> images, String detail) {
|
||||
String paramJson = buildChatVisionRequestBody(prompt, images, detail);
|
||||
final HttpResponse response = sendPost(CHAT_ENDPOINT, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String models() {
|
||||
final HttpResponse response = sendGet(MODELS_ENDPOINT);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModel(String modelId) {
|
||||
final HttpResponse response = sendGet(MODELS_ENDPOINT + "/" + modelId);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String languageModels() {
|
||||
final HttpResponse response = sendGet(LANGUAGE_MODELS);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageModel(String modelId) {
|
||||
final HttpResponse response = sendGet(LANGUAGE_MODELS + "/" + modelId);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tokenizeText(String text) {
|
||||
String paramJson = buildTokenizeRequestBody(text);
|
||||
final HttpResponse response = sendPost(TOKENIZE_TEXT, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deferredCompletion(String requestId) {
|
||||
final HttpResponse response = sendGet(DEFERRED_COMPLETION + "/" + requestId);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
// 构建chat请求体
|
||||
private String buildChatRequestBody(final List<Message> messages) {
|
||||
//使用JSON工具
|
||||
final 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, final List<String> images, String detail) {
|
||||
// 定义消息结构
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
final List<Object> content = new ArrayList<>();
|
||||
|
||||
final 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工具
|
||||
final 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(final List<Message> messages, int maxToken) {
|
||||
final 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工具
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("text", text);
|
||||
//合并其他参数
|
||||
paramMap.putAll(config.getAdditionalConfigMap());
|
||||
|
||||
return JSONUtil.toJsonStr(paramMap);
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 对grok的封装实现
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.grok;
|
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.openai;
|
||||
|
||||
/**
|
||||
* openai公共类
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.openai;
|
||||
|
||||
import cn.hutool.ai.Models;
|
||||
import cn.hutool.ai.core.BaseConfig;
|
||||
|
||||
/**
|
||||
* openai配置类,初始化API接口地址,设置默认的模型
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.openai;
|
||||
|
||||
import cn.hutool.ai.core.AIConfig;
|
||||
import cn.hutool.ai.core.AIServiceProvider;
|
||||
|
||||
/**
|
||||
* 创建Openai服务实现类
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public class OpenaiProvider implements AIServiceProvider {
|
||||
|
||||
@Override
|
||||
public String getServiceName() {
|
||||
return "openai";
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpenaiService create(final AIConfig config) {
|
||||
return new OpenaiServiceImpl(config);
|
||||
}
|
||||
}
|
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.openai;
|
||||
|
||||
import cn.hutool.ai.core.AIService;
|
||||
import cn.hutool.ai.core.Message;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* openai支持的扩展接口
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
public interface OpenaiService extends AIService {
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 图片列表/或者图片Base64编码图片列表(URI形式)
|
||||
* @param detail 手动设置图片的质量,取值范围high、low、auto,默认为auto
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String chatVision(String prompt, final List<String> images, String detail);
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 传入的图片列表地址/或者图片Base64编码图片列表(URI形式)
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
default String chatVision(String prompt, final List<String> images) {
|
||||
return chatVision(prompt, images, OpenaiCommon.OpenaiVision.AUTO.getDetail());
|
||||
}
|
||||
|
||||
/**
|
||||
* 文生图 请设置config中model为支持图片功能的模型 DALL·E系列
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @return 包含生成图片的url
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String imagesGenerations(String prompt);
|
||||
|
||||
/**
|
||||
* 图片编辑 该方法仅支持 DALL·E 2 model
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param image 需要编辑的图像必须是 PNG 格式
|
||||
* @param mask 如果提供,则是一个与编辑图像大小相同的遮罩图像应该是灰度图,白色表示需要编辑的区域,黑色表示不需要编辑的区域。
|
||||
* @return 包含生成图片的url
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String imagesEdits(String prompt, final File image, final File mask);
|
||||
|
||||
/**
|
||||
* 图片编辑 该方法仅支持 DALL·E 2 model
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param image 需要编辑的图像必须是 PNG 格式
|
||||
* @return 包含生成图片的url
|
||||
* @since 5.8.38
|
||||
*/
|
||||
default String imagesEdits(String prompt, final File image) {
|
||||
return imagesEdits(prompt, image, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片变形 该方法仅支持 DALL·E 2 model
|
||||
*
|
||||
* @param image 需要变形的图像必须是 PNG 格式
|
||||
* @return 包含生成图片的url
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String imagesVariations(final File image);
|
||||
|
||||
/**
|
||||
* TTS文本转语音 请设置config中model为支持TTS功能的模型 TTS系列
|
||||
*
|
||||
* @param input 需要转成语音的文本
|
||||
* @param voice AI的音色
|
||||
* @return 返回的音频mp3文件流
|
||||
* @since 5.8.38
|
||||
*/
|
||||
InputStream textToSpeech(String input, final OpenaiCommon.OpenaiSpeech voice);
|
||||
|
||||
/**
|
||||
* TTS文本转语音 请设置config中model为支持TTS功能的模型 TTS系列
|
||||
*
|
||||
* @param input 需要转成语音的文本
|
||||
* @return 返回的音频mp3文件流
|
||||
* @since 5.8.38
|
||||
*/
|
||||
default InputStream textToSpeech(String input) {
|
||||
return textToSpeech(input, OpenaiCommon.OpenaiSpeech.ALLOY);
|
||||
}
|
||||
|
||||
/**
|
||||
* STT音频转文本 请设置config中model为支持STT功能的模型 whisper
|
||||
*
|
||||
* @param file 需要转成文本的音频文件
|
||||
* @return 返回的文本内容
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String speechToText(final File file);
|
||||
|
||||
/**
|
||||
* 文本向量化 请设置config中model为支持文本向量化功能的模型 text-embedding系列
|
||||
*
|
||||
* @param input 需要向量化的内容
|
||||
* @return 处理后的向量信息
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String embeddingText(String input);
|
||||
|
||||
/**
|
||||
* 检查文本或图像是否具有潜在的危害性
|
||||
* 仅支持omni-moderation-latest和text-moderation-latest模型
|
||||
*
|
||||
* @param text 需要检查的文本
|
||||
* @param imgUrl 需要检查的图片地址
|
||||
* @return AI返回结果
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String moderations(String text, String imgUrl);
|
||||
|
||||
/**
|
||||
* 检查文本是否具有潜在的危害性
|
||||
* 仅支持omni-moderation-latest和text-moderation-latest模型
|
||||
*
|
||||
* @param text 需要检查的文本
|
||||
* @return AI返回结果
|
||||
* @since 5.8.38
|
||||
*/
|
||||
default String moderations(String text) {
|
||||
return moderations(text, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 推理chat
|
||||
* 支持o3-mini和o1
|
||||
*
|
||||
* @param prompt 对话题词
|
||||
* @param reasoningEffort 推理程度
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String chatReasoning(String prompt, String reasoningEffort);
|
||||
|
||||
/**
|
||||
* 推理chat
|
||||
* 支持o3-mini和o1
|
||||
*
|
||||
* @param prompt 对话题词
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
default String chatReasoning(String prompt) {
|
||||
return chatReasoning(prompt, OpenaiCommon.OpenaiReasoning.MEDIUM.getEffort());
|
||||
}
|
||||
|
||||
/**
|
||||
* 推理chat
|
||||
* 支持o3-mini和o1
|
||||
*
|
||||
* @param messages 消息列表
|
||||
* @param reasoningEffort 推理程度
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
String chatReasoning(final List<Message> messages, String reasoningEffort);
|
||||
|
||||
/**
|
||||
* 推理chat
|
||||
* 支持o3-mini和o1
|
||||
*
|
||||
* @param messages 消息列表
|
||||
* @return AI回答
|
||||
* @since 5.8.38
|
||||
*/
|
||||
default String chatReasoning(final List<Message> messages) {
|
||||
return chatReasoning(messages, OpenaiCommon.OpenaiReasoning.MEDIUM.getEffort());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.openai;
|
||||
|
||||
import cn.hutool.ai.core.AIConfig;
|
||||
import cn.hutool.ai.core.BaseAIService;
|
||||
import cn.hutool.ai.core.Message;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.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 5.8.38
|
||||
*/
|
||||
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(final AIConfig config) {
|
||||
//初始化Openai客户端
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chat(String prompt) {
|
||||
// 定义消息结构
|
||||
final 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(final List<Message> messages) {
|
||||
String paramJson = buildChatRequestBody(messages);
|
||||
final HttpResponse response = sendPost(CHAT_ENDPOINT, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chatVision(String prompt, final List<String> images, String detail) {
|
||||
String paramJson = buildChatVisionRequestBody(prompt, images, detail);
|
||||
final HttpResponse response = sendPost(CHAT_ENDPOINT, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String imagesGenerations(String prompt) {
|
||||
String paramJson = buildImagesGenerationsRequestBody(prompt);
|
||||
final HttpResponse response = sendPost(IMAGES_GENERATIONS, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String imagesEdits(String prompt, final File image, final File mask) {
|
||||
final Map<String, Object> paramMap = buildImagesEditsRequestBody(prompt, image, mask);
|
||||
final HttpResponse response = sendFormData(IMAGES_EDITS, paramMap);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String imagesVariations(final File image) {
|
||||
final Map<String, Object> paramMap = buildImagesVariationsRequestBody(image);
|
||||
final HttpResponse response = sendFormData(IMAGES_VARIATIONS, paramMap);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream textToSpeech(String input, final OpenaiCommon.OpenaiSpeech voice) {
|
||||
String paramJson = buildTTSRequestBody(input, voice.getVoice());
|
||||
final HttpResponse response = sendPost(TTS, paramJson);
|
||||
return response.bodyStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String speechToText(final File file) {
|
||||
final Map<String, Object> paramMap = buildSTTRequestBody(file);
|
||||
final HttpResponse response = sendFormData(STT, paramMap);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String embeddingText(String input) {
|
||||
String paramJson = buildEmbeddingTextRequestBody(input);
|
||||
final HttpResponse response = sendPost(EMBEDDINGS, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String moderations(String text, String imgUrl) {
|
||||
String paramJson = buileModerationsRequestBody(text, imgUrl);
|
||||
final HttpResponse response = sendPost(MODERATIONS, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chatReasoning(String prompt, String reasoningEffort) {
|
||||
// 定义消息结构
|
||||
final 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(final List<Message> messages, String reasoningEffort) {
|
||||
String paramJson = buildChatReasoningRequestBody(messages, reasoningEffort);
|
||||
final HttpResponse response = sendPost(CHAT_ENDPOINT, paramJson);
|
||||
return response.body();
|
||||
}
|
||||
|
||||
// 构建chat请求体
|
||||
private String buildChatRequestBody(final List<Message> messages) {
|
||||
//使用JSON工具
|
||||
final 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, final List<String> images, String detail) {
|
||||
// 定义消息结构
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
final List<Object> content = new ArrayList<>();
|
||||
|
||||
final Map<String, String> contentMap = new HashMap<>();
|
||||
contentMap.put("type", "text");
|
||||
contentMap.put("text", prompt);
|
||||
content.add(contentMap);
|
||||
for (String img : images) {
|
||||
final Map<String, Object> imgUrlMap = new HashMap<>();
|
||||
imgUrlMap.put("type", "image_url");
|
||||
final Map<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工具
|
||||
final 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) {
|
||||
final 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, final File image, final File mask) {
|
||||
final 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(final File image) {
|
||||
final 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) {
|
||||
final 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(final File file) {
|
||||
final 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工具
|
||||
final 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工具
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("model", config.getModel());
|
||||
|
||||
final List<Object> input = new ArrayList<>();
|
||||
//添加文本参数
|
||||
if (!StrUtil.isBlank(text)) {
|
||||
final Map<String, String> textMap = new HashMap<>();
|
||||
textMap.put("type", "text");
|
||||
textMap.put("text", text);
|
||||
input.add(textMap);
|
||||
}
|
||||
//添加图片参数
|
||||
if (!StrUtil.isBlank(imgUrl)) {
|
||||
final Map<String, Object> imgUrlMap = new HashMap<>();
|
||||
imgUrlMap.put("type", "image_url");
|
||||
final Map<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(final List<Message> messages, String reasoningEffort) {
|
||||
final 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);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 对openai的封装实现
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.openai;
|
24
hutool-ai/src/main/java/cn/hutool/ai/model/package-info.java
Normal file
24
hutool-ai/src/main/java/cn/hutool/ai/model/package-info.java
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 对各个AI大模型的相关封装
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model;
|
24
hutool-ai/src/main/java/cn/hutool/ai/package-info.java
Normal file
24
hutool-ai/src/main/java/cn/hutool/ai/package-info.java
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Hutool-ai主要用于AI大模型的封装,只需要对AI模型最基本的设置,即可调用AI大模型。
|
||||
*
|
||||
* @author elichow
|
||||
* @since 5.8.38
|
||||
*/
|
||||
|
||||
package cn.hutool.ai;
|
@@ -0,0 +1,4 @@
|
||||
cn.hutool.ai.model.deepseek.DeepSeekConfig
|
||||
cn.hutool.ai.model.openai.OpenaiConfig
|
||||
cn.hutool.ai.model.doubao.DoubaoConfig
|
||||
cn.hutool.ai.model.grok.GrokConfig
|
@@ -0,0 +1,4 @@
|
||||
cn.hutool.ai.model.deepseek.DeepSeekProvider
|
||||
cn.hutool.ai.model.openai.OpenaiProvider
|
||||
cn.hutool.ai.model.doubao.DoubaoProvider
|
||||
cn.hutool.ai.model.grok.GrokProvider
|
41
hutool-ai/src/test/java/AIServiceFactoryTest.java
Normal file
41
hutool-ai/src/test/java/AIServiceFactoryTest.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import cn.hutool.ai.AIServiceFactory;
|
||||
import cn.hutool.ai.ModelName;
|
||||
import cn.hutool.ai.core.AIConfigBuilder;
|
||||
import cn.hutool.ai.core.AIService;
|
||||
import cn.hutool.ai.model.deepseek.DeepSeekService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
class AIServiceFactoryTest {
|
||||
|
||||
String key = "your key";
|
||||
|
||||
@Test
|
||||
void getAIService() {
|
||||
final AIService aiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DEEPSEEK.getValue()).setApiKey(key).build());
|
||||
assertNotNull(aiService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAIService() {
|
||||
final DeepSeekService deepSeekService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DEEPSEEK.getValue()).setApiKey(key).build(), DeepSeekService.class);
|
||||
assertNotNull(deepSeekService);
|
||||
}
|
||||
}
|
87
hutool-ai/src/test/java/AIUtilTest.java
Normal file
87
hutool-ai/src/test/java/AIUtilTest.java
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import cn.hutool.ai.AIUtil;
|
||||
import cn.hutool.ai.ModelName;
|
||||
import cn.hutool.ai.core.AIConfigBuilder;
|
||||
import cn.hutool.ai.core.AIService;
|
||||
import cn.hutool.ai.core.Message;
|
||||
import cn.hutool.ai.model.deepseek.DeepSeekService;
|
||||
import cn.hutool.ai.model.doubao.DoubaoService;
|
||||
import cn.hutool.ai.model.grok.GrokService;
|
||||
import cn.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.assertNotNull;
|
||||
|
||||
class AIUtilTest {
|
||||
|
||||
String key = "your key";
|
||||
|
||||
@Test
|
||||
void getAIService() {
|
||||
final DeepSeekService deepSeekService = AIUtil.getAIService(new AIConfigBuilder(ModelName.DEEPSEEK.getValue()).setApiKey(key).build(), DeepSeekService.class);
|
||||
assertNotNull(deepSeekService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetAIService() {
|
||||
final AIService aiService = AIUtil.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue()).setApiKey(key).build());
|
||||
assertNotNull(aiService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getDeepSeekService() {
|
||||
final DeepSeekService deepSeekService = AIUtil.getDeepSeekService(new AIConfigBuilder(ModelName.DEEPSEEK.getValue()).setApiKey(key).build());
|
||||
assertNotNull(deepSeekService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getDoubaoService() {
|
||||
final DoubaoService doubaoService = AIUtil.getDoubaoService(new AIConfigBuilder(ModelName.DOUBAO.getValue()).setApiKey(key).build());
|
||||
assertNotNull(doubaoService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getGrokService() {
|
||||
final GrokService grokService = AIUtil.getGrokService(new AIConfigBuilder(ModelName.GROK.getValue()).setApiKey(key).build());
|
||||
assertNotNull(grokService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getOpenAIService() {
|
||||
final OpenaiService openAIService = AIUtil.getOpenAIService(new AIConfigBuilder(ModelName.OPENAI.getValue()).setApiKey(key).build());
|
||||
assertNotNull(openAIService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void chat() {
|
||||
final String chat = AIUtil.chat(new AIConfigBuilder(ModelName.DEEPSEEK.getValue()).setApiKey(key).build(), "写一首赞美我的诗");
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testChat() {
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system","你是财神爷,只会说“我是财神”"));
|
||||
messages.add(new Message("user","你是谁啊?"));
|
||||
final String chat = AIUtil.chat(new AIConfigBuilder(ModelName.DEEPSEEK.getValue()).setApiKey(key).build(), messages);
|
||||
assertNotNull(chat);
|
||||
}
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.deepseek;
|
||||
|
||||
import cn.hutool.ai.AIServiceFactory;
|
||||
import cn.hutool.ai.ModelName;
|
||||
import cn.hutool.ai.core.AIConfigBuilder;
|
||||
import cn.hutool.ai.core.Message;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
class DeepSeekServiceTest {
|
||||
|
||||
String key = "your key";
|
||||
DeepSeekService deepSeekService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DEEPSEEK.getValue()).setApiKey(key).build(),DeepSeekService.class);
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chat(){
|
||||
final String chat = deepSeekService.chat("写一个疯狂星期四广告词");
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testChat(){
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system","你是个抽象大师,会说很抽象的话,最擅长说抽象的笑话"));
|
||||
messages.add(new Message("user","给我说一个笑话"));
|
||||
final String chat = deepSeekService.chat(messages);
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void beta() {
|
||||
final String beta = deepSeekService.beta("写一个疯狂星期四广告词");
|
||||
System.out.println(beta);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void models() {
|
||||
final String models = deepSeekService.models();
|
||||
assertNotNull(models);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void balance() {
|
||||
final String balance = deepSeekService.balance();
|
||||
assertNotNull(balance);
|
||||
}
|
||||
}
|
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.doubao;
|
||||
|
||||
import cn.hutool.ai.AIServiceFactory;
|
||||
import cn.hutool.ai.ModelName;
|
||||
import cn.hutool.ai.Models;
|
||||
import cn.hutool.ai.core.AIConfigBuilder;
|
||||
import cn.hutool.ai.core.Message;
|
||||
import cn.hutool.core.img.ImgUtil;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
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.assertNotNull;
|
||||
|
||||
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
|
||||
@Disabled
|
||||
void chat(){
|
||||
final String chat = doubaoService.chat("写一个疯狂星期四广告词");
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testChat(){
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system","你是个抽象大师,会说很抽象的话,最擅长说抽象的笑话"));
|
||||
messages.add(new Message("user","给我说一个笑话"));
|
||||
final String chat = doubaoService.chat(messages);
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chatVision() {
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).setModel(Models.Doubao.DOUBAO_1_5_VISION_PRO_32K.getModel()).build(), DoubaoService.class);
|
||||
final String base64 = ImgUtil.toBase64DataUri(Toolkit.getDefaultToolkit().createImage("your imageUrl"), "png");
|
||||
final String chatVision = doubaoService.chatVision("图片上有些什么?", Arrays.asList(base64));
|
||||
assertNotNull(chatVision);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testChatVision() {
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).setModel(Models.Doubao.DOUBAO_1_5_VISION_PRO_32K.getModel()).build(), DoubaoService.class);
|
||||
final 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());
|
||||
assertNotNull(chatVision);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void videoTasks() {
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).setModel("your Endpoint ID").build(), DoubaoService.class);
|
||||
final String videoTasks = doubaoService.videoTasks("生成一段动画视频,主角是大耳朵图图,一个活泼可爱的小男孩。视频中图图在公园里玩耍," +
|
||||
"画面采用明亮温暖的卡通风格,色彩鲜艳,动作流畅。背景音乐轻快活泼,带有冒险感,音效包括鸟叫声、欢笑声和山洞回声。", "https://img2.baidu.com/it/u=862000265,4064861820&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1544");
|
||||
assertNotNull(videoTasks);//cgt-20250306170051-6r9gk
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void getVideoTasksInfo() {
|
||||
//cgt-20250306170051-6r9gk
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).build(), DoubaoService.class);
|
||||
final String videoTasksInfo = doubaoService.getVideoTasksInfo("cgt-20250306170051-6r9gk");
|
||||
assertNotNull(videoTasksInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void embeddingText() {
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).setModel(Models.Doubao.DOUBAO_EMBEDDING_TEXT_240715.getModel()).build(), DoubaoService.class);
|
||||
final String embeddingText = doubaoService.embeddingText(new String[]{"阿斯顿", "马丁"});
|
||||
assertNotNull(embeddingText);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void embeddingVision() {
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).setModel(Models.Doubao.DOUBAO_EMBEDDING_VISION.getModel()).build(), DoubaoService.class);
|
||||
final String embeddingVision = doubaoService.embeddingVision("天空好难", "https://img2.baidu.com/it/u=862000265,4064861820&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1544");
|
||||
assertNotNull(embeddingVision);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void botsChat() {
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).setModel("your bots id").build(), DoubaoService.class);
|
||||
final ArrayList<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system","你是什么都可以"));
|
||||
messages.add(new Message("user","你想做些什么"));
|
||||
final String botsChat = doubaoService.botsChat(messages);
|
||||
assertNotNull(botsChat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void tokenization() {
|
||||
final String tokenization = doubaoService.tokenization(new String[]{"阿斯顿", "马丁"});
|
||||
assertNotNull(tokenization);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void batchChat() {
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).setModel("your Endpoint ID").build(), DoubaoService.class);
|
||||
final String batchChat = doubaoService.batchChat("写首歌词");
|
||||
assertNotNull(batchChat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testBatchChat() {
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).setModel("your Endpoint ID").build(), DoubaoService.class);
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system","你是个抽象大师"));
|
||||
messages.add(new Message("user","写一个KFC的抽象广告"));
|
||||
final String batchChat = doubaoService.batchChat(messages);
|
||||
assertNotNull(batchChat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void createContext() {
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).setModel("your Endpoint ID").build(), DoubaoService.class);
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system","你是个抽象大师,你真的很抽象"));
|
||||
final String context = doubaoService.createContext(messages);//ctx-20250307092153-cvslm
|
||||
assertNotNull(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testCreateContext() {
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).setModel("your Endpoint ID").build(), DoubaoService.class);
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system","你是个抽象大师,你真的很抽象"));
|
||||
final String context = doubaoService.createContext(messages,DoubaoCommon.DoubaoContext.COMMON_PREFIX.getMode());
|
||||
assertNotNull(context);//ctx-20250307092153-cvslm
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chatContext() {
|
||||
//ctx-20250307092153-cvslm
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).setModel("eyour Endpoint ID").build(), DoubaoService.class);
|
||||
final String chatContext = doubaoService.chatContext("你是谁?", "ctx-20250307092153-cvslm");
|
||||
assertNotNull(chatContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testChatContext() {
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).setModel("your Endpoint ID").build(), DoubaoService.class);
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("user","你怎么看待意大利面拌水泥?"));
|
||||
final String chatContext = doubaoService.chatContext(messages, "ctx-20250307092153-cvslm");
|
||||
assertNotNull(chatContext);
|
||||
}
|
||||
}
|
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.grok;
|
||||
|
||||
import cn.hutool.ai.AIServiceFactory;
|
||||
import cn.hutool.ai.ModelName;
|
||||
import cn.hutool.ai.Models;
|
||||
import cn.hutool.ai.core.AIConfigBuilder;
|
||||
import cn.hutool.ai.core.Message;
|
||||
import cn.hutool.core.img.ImgUtil;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
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.assertNotNull;
|
||||
|
||||
class GrokServiceTest {
|
||||
|
||||
String key = "your key";
|
||||
GrokService grokService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.GROK.getValue()).setApiKey(key).build(), GrokService.class);
|
||||
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chat(){
|
||||
final String chat = grokService.chat("写一个疯狂星期四广告词");
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testChat(){
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system","你是个抽象大师,会说很抽象的话,最擅长说抽象的笑话"));
|
||||
messages.add(new Message("user","给我说一个笑话"));
|
||||
final String chat = grokService.chat(messages);
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void message() {
|
||||
final String message = grokService.message("给我一个KFC的广告词", 4096);
|
||||
assertNotNull(message);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chatVision() {
|
||||
final GrokService grokService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.GROK.getValue()).setModel(Models.Grok.GROK_2_VISION_1212.getModel()).setApiKey(key).build(), GrokService.class);
|
||||
final String base64 = ImgUtil.toBase64DataUri(Toolkit.getDefaultToolkit().createImage("your imageUrl"), "png");
|
||||
final String chatVision = grokService.chatVision("图片上有些什么?", Arrays.asList(base64));
|
||||
assertNotNull(chatVision);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testChatVision() {
|
||||
final GrokService grokService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.GROK.getValue()).setModel(Models.Grok.GROK_2_VISION_1212.getModel()).setApiKey(key).build(), GrokService.class);
|
||||
final 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"));
|
||||
assertNotNull(chatVision);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void models() {
|
||||
final String models = grokService.models();
|
||||
assertNotNull(models);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void getModel() {
|
||||
final String model = grokService.getModel("");
|
||||
assertNotNull(model);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void languageModels() {
|
||||
final String languageModels = grokService.languageModels();
|
||||
assertNotNull(languageModels);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void getLanguageModel() {
|
||||
final String language = grokService.getLanguageModel("");
|
||||
assertNotNull(language);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void tokenizeText() {
|
||||
final String tokenizeText = grokService.tokenizeText(key);
|
||||
assertNotNull(tokenizeText);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void deferredCompletion() {
|
||||
final String deferred = grokService.deferredCompletion(key);
|
||||
assertNotNull(deferred);
|
||||
}
|
||||
}
|
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.ai.model.openai;
|
||||
|
||||
import cn.hutool.ai.AIServiceFactory;
|
||||
import cn.hutool.ai.ModelName;
|
||||
import cn.hutool.ai.Models;
|
||||
import cn.hutool.ai.core.AIConfigBuilder;
|
||||
import cn.hutool.ai.core.Message;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
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;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
class OpenaiServiceTest {
|
||||
|
||||
String key = "your key";
|
||||
OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue()).setApiKey(key).build(), OpenaiService.class);
|
||||
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chat(){
|
||||
final String chat = openaiService.chat("写一个疯狂星期四广告词");
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testChat(){
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system","你是个抽象大师,会说很抽象的话,最擅长说抽象的笑话"));
|
||||
messages.add(new Message("user","给我说一个笑话"));
|
||||
final String chat = openaiService.chat(messages);
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chatVision() {
|
||||
final OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
|
||||
.setApiKey(key).setModel(Models.Openai.GPT_4O_MINI.getModel()).build(), OpenaiService.class);
|
||||
final 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"));
|
||||
assertNotNull(chatVision);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void imagesGenerations() {
|
||||
final OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
|
||||
.setApiKey(key).setModel(Models.Openai.DALL_E_3.getModel()).build(), OpenaiService.class);
|
||||
final String imagesGenerations = openaiService.imagesGenerations("一位年轻的宇航员站在未来感十足的太空站内,透过巨大的弧形落地窗凝望浩瀚宇宙。窗外,璀璨的星河与五彩斑斓的星云交织,远处隐约可见未知星球的轮廓,仿佛在召唤着探索的脚步。宇航服上的呼吸灯与透明显示屏上的星图交相辉映,象征着人类科技与宇宙奥秘的碰撞。画面深邃而神秘,充满对未知的渴望与无限可能的想象。");
|
||||
assertNotNull(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
|
||||
@Disabled
|
||||
void imagesEdits() {
|
||||
final OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
|
||||
.setApiKey(key).setModel(Models.Openai.DALL_E_2.getModel()).build(), OpenaiService.class);
|
||||
final File file = FileUtil.file("your imgUrl");
|
||||
final String imagesEdits = openaiService.imagesEdits("茂密的森林中,有一只九色鹿若隐若现",file);
|
||||
assertNotNull(imagesEdits);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void imagesVariations() {
|
||||
final OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
|
||||
.setApiKey(key).setModel(Models.Openai.DALL_E_2.getModel()).build(), OpenaiService.class);
|
||||
final File file = FileUtil.file("your imgUrl");
|
||||
final String imagesVariations = openaiService.imagesVariations(file);
|
||||
assertNotNull(imagesVariations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void textToSpeech() {
|
||||
final OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
|
||||
.setApiKey(key).setModel(Models.Openai.TTS_1_HD.getModel()).build(), OpenaiService.class);
|
||||
final InputStream inputStream = openaiService.textToSpeech("万里山河一夜白,\n" +
|
||||
"千峰尽染玉龙哀。\n" +
|
||||
"长风卷起琼花碎,\n" +
|
||||
"直上九霄揽月来。", OpenaiCommon.OpenaiSpeech.NOVA);
|
||||
|
||||
final String filePath = "your filePath";
|
||||
final Path path = Paths.get(filePath);
|
||||
try (final FileOutputStream outputStream = new FileOutputStream(filePath)) {
|
||||
Files.createDirectories(path.getParent());
|
||||
final byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void speechToText() {
|
||||
final OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
|
||||
.setApiKey(key).setModel(Models.Openai.WHISPER_1.getModel()).build(), OpenaiService.class);
|
||||
final File file = FileUtil.file("your filePath");
|
||||
final String speechToText = openaiService.speechToText(file);
|
||||
System.out.println(speechToText);
|
||||
assertNotNull(speechToText);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void embeddingText() {
|
||||
final OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
|
||||
.setApiKey(key).setModel(Models.Openai.TEXT_EMBEDDING_3_SMALL.getModel()).build(), OpenaiService.class);
|
||||
final String embeddingText = openaiService.embeddingText("萬里山河一夜白,千峰盡染玉龍哀,長風捲起瓊花碎,直上九霄闌月來");
|
||||
assertNotNull(embeddingText);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void moderations() {
|
||||
final OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
|
||||
.setApiKey(key).setModel(Models.Openai.OMNI_MODERATION_LATEST.getModel()).build(), OpenaiService.class);
|
||||
final String moderations = openaiService.moderations("你要杀人", "https://img2.baidu.com/it/u=862000265,4064861820&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1544");
|
||||
assertNotNull(moderations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chatReasoning() {
|
||||
final OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
|
||||
.setApiKey(key).setModel(Models.Openai.O3_MINI.getModel()).build(), OpenaiService.class);
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system","你是现代抽象家"));
|
||||
messages.add(new Message("user","给我一个KFC疯狂星期四的文案"));
|
||||
final String chatReasoning = openaiService.chatReasoning(messages, OpenaiCommon.OpenaiReasoning.HIGH.getEffort());
|
||||
assertNotNull(chatReasoning);
|
||||
}
|
||||
}
|
@@ -113,6 +113,11 @@
|
||||
<artifactId>hutool-jwt</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-ai</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
Reference in New Issue
Block a user