mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-08-18 20:38:02 +08:00
Compare commits
34 Commits
bedfa07a8a
...
v6-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa8f1f2091 | ||
|
|
f5910f9db4 | ||
|
|
6f7d4f1402 | ||
|
|
b05f45a76f | ||
|
|
9fbf8d85d1 | ||
|
|
667915d05c | ||
|
|
6574dd010b | ||
|
|
90b83d16a5 | ||
|
|
8aff7bfb66 | ||
|
|
d2d5e3092f | ||
|
|
bf7777cdc9 | ||
|
|
c64e48e037 | ||
|
|
04a0b342e4 | ||
|
|
8be799e685 | ||
|
|
ea09b76332 | ||
|
|
8595c0a988 | ||
|
|
b0b44e2ec6 | ||
|
|
cdfdf6f5bd | ||
|
|
d98cded437 | ||
|
|
7c74c789e1 | ||
|
|
115b4b91c4 | ||
|
|
e019d98e3e | ||
|
|
fbd6821280 | ||
|
|
cf58e5e41e | ||
|
|
9f76238137 | ||
|
|
b9cedd8798 | ||
|
|
e91dc5b756 | ||
|
|
4f68e8f83e | ||
|
|
4aea2ef471 | ||
|
|
3658d9f7ca | ||
|
|
49e9ede07b | ||
|
|
58789b61f5 | ||
|
|
761c8c1757 | ||
|
|
ca4cdcb80a |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -1,4 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [Looly]
|
||||
custom: ['https://gitee.com/chinabugotech/hutool']
|
||||
custom: ['https://gitee.com/dromara/hutool', 'https://dromara.gitee.io/donate.html']
|
||||
|
||||
@@ -3,11 +3,7 @@
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
# 6.0.0-M17 (2024-10-09)
|
||||
|
||||
### 计划实现
|
||||
* 【db 】 增加DDL封装
|
||||
* 【db 】 Entity数据量大时占用较多内存,考虑共享meta信息
|
||||
# 6.0.0-M22 (2025-05-07)
|
||||
|
||||
### ❌不兼容特性
|
||||
|
||||
|
||||
38
README-EN.md
Normal file → Executable file
38
README-EN.md
Normal file → Executable file
@@ -33,11 +33,11 @@
|
||||
<a target="_blank" href='https://gitee.com/dromara/hutool/stargazers'>
|
||||
<img src='https://gitee.com/dromara/hutool/badge/star.svg?theme=gvp' alt='star'/>
|
||||
</a>
|
||||
<a target="_blank" href='https://github.com/chinabugotech/hutool'>
|
||||
<img src="https://img.shields.io/github/stars/chinabugotech/hutool.svg?style=social" alt="github star"/>
|
||||
<a target="_blank" href='https://github.com/dromara/hutool'>
|
||||
<img src="https://img.shields.io/github/stars/dromara/hutool.svg?style=social" alt="github star"/>
|
||||
</a>
|
||||
<a target="_blank" href='https://gitcode.com/chinabugotech/hutool'>
|
||||
<img src="https://gitcode.com/chinabugotech/hutool/star/badge.svg" alt="gitcode star"/>
|
||||
<a target="_blank" href='https://gitcode.com/dromara/hutool'>
|
||||
<img src="https://gitcode.com/dromara/hutool/star/badge.svg" alt="gitcode star"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -126,32 +126,32 @@ Each module can be introduced individually, or all modules can be introduced by
|
||||
### 🍊Maven
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>cn.hutool.v7</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>7.0.0-M1</version>
|
||||
<groupId>org.dromara.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>6.0.0-M23</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### 🍐Gradle
|
||||
```
|
||||
implementation 'cn.hutool.v7:hutool-all:7.0.0-M1'
|
||||
implementation 'org.dromara.hutool:hutool-all:6.0.0-M23'
|
||||
```
|
||||
|
||||
## 📥Download
|
||||
|
||||
- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/v7/hutool-all/7.0.0-M1/)
|
||||
- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/6.0.0-M23/)
|
||||
|
||||
> 🔔️note:
|
||||
> Hutool 7.x supports JDK17 and is not tested on Android platforms, and cannot guarantee that all tool classes or tool methods are available.
|
||||
> Hutool 5.x supports JDK8+ and is not tested on Android platforms, and cannot guarantee that all tool classes or tool methods are available.
|
||||
> If your project uses JDK7, please use Hutool 4.x version.
|
||||
|
||||
### 🚽Compile and install
|
||||
|
||||
Download the entire project source code
|
||||
|
||||
gitee:[https://gitee.com/chinabugotech/hutool](https://gitee.com/chinabugotech/hutool)
|
||||
gitee:[https://gitee.com/dromara/hutool](https://gitee.com/dromara/hutool)
|
||||
|
||||
github:[https://github.com/chinabugotech/hutool](https://github.com/chinabugotech/hutool)
|
||||
github:[https://github.com/dromara/hutool](https://github.com/dromara/hutool)
|
||||
|
||||
```sh
|
||||
cd ${hutool}
|
||||
@@ -168,16 +168,16 @@ Hutool's source code is divided into two branches:
|
||||
|
||||
| branch | description |
|
||||
|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| v7-master | The master branch, the branch used by the release version, is the same as the jar committed to the central repository and does not receive any pr or modifications. |
|
||||
| v7-dev | Development branch, which defaults to the next SNAPSHOT version, accepts modifications or pr |
|
||||
| v6-master | The master branch, the branch used by the release version, is the same as the jar committed to the central repository and does not receive any pr or modifications. |
|
||||
| v6-dev | Development branch, which defaults to the next SNAPSHOT version, accepts modifications or pr |
|
||||
|
||||
### 🐞Provide feedback or suggestions on bugs
|
||||
|
||||
When submitting feedback, please indicate which JDK version, Hutool version, and related dependency library version you are using.
|
||||
|
||||
- [Gitee issue](https://gitee.com/chinabugotech/hutool/issues)
|
||||
- [Github issue](https://github.com/chinabugotech/hutool/issues)
|
||||
- [Gitcode issue](https://gitcode.com/chinabugotech/hutool/issues)
|
||||
- [Gitee issue](https://gitee.com/dromara/hutool/issues)
|
||||
- [Github issue](https://github.com/dromara/hutool/issues)
|
||||
- [Gitcode issue](https://gitcode.com/dromara/hutool/issues)
|
||||
|
||||
### 🧬Principles of PR(pull request)
|
||||
|
||||
@@ -186,13 +186,13 @@ Hutool welcomes anyone to contribute code to Hutool, but the author suffers from
|
||||
1. Improve the comments, especially each new method should follow the Java documentation specification to indicate the method description, parameter description, return value description and other information, if necessary, please add unit tests, if you want, you can also add your name.
|
||||
2. Code indentation according to Eclipse.
|
||||
3. Newly added methods do not use third-party library methods,Unless the method tool is add to the '**extra module**'.
|
||||
4. Please pull request to the `v7-dev` branch. Hutool uses a new branch after 7.x: `v7-master` is the master branch, which indicates the version of the central library that has been released, and this branch does not allow pr or modifications.
|
||||
4. Please pull request to the `v6-dev` branch. Hutool uses a new branch after 5.x: `v6-master` is the master branch, which indicates the version of the central library that has been released, and this branch does not allow pr or modifications.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
## ⭐Star Hutool
|
||||
|
||||
[](https://starchart.cc/chinabugotech/hutool)
|
||||
[](https://starchart.cc/dromara/hutool)
|
||||
|
||||
## 📌WeChat Official Account
|
||||
|
||||
|
||||
61
README.md
Normal file → Executable file
61
README.md
Normal file → Executable file
@@ -9,8 +9,8 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a target="_blank" href="https://search.maven.org/artifact/cn.hutool/hutool-all">
|
||||
<img alt="" src="https://img.shields.io/maven-central/v/cn.hutool/hutool-all.svg?label=Maven%20Central" />
|
||||
<a target="_blank" href="https://search.maven.org/artifact/org.dromara.hutool/hutool-all">
|
||||
<img alt="" src="https://img.shields.io/maven-central/v/org.dromara.hutool/hutool-all.svg?label=Maven%20Central" />
|
||||
</a>
|
||||
<a target="_blank" href="https://www.apache.org/licenses/LICENSE-2.0.html">
|
||||
<img alt="" src="https://img.shields.io/:license-apache2.0-blue.svg?logo=apache" />
|
||||
@@ -18,26 +18,26 @@
|
||||
<a target="_blank" href="https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html">
|
||||
<img alt="" src="https://img.shields.io/badge/JDK-8+-green.svg" />
|
||||
</a>
|
||||
<a target="_blank" href="https://travis-ci.com/chinabugotech/hutool">
|
||||
<img alt="" src="https://travis-ci.com/chinabugotech/hutool.svg?branch=v5-master" />
|
||||
<a target="_blank" href="https://travis-ci.com/dromara/hutool">
|
||||
<img alt="" src="https://travis-ci.com/dromara/hutool.svg?branch=v5-master" />
|
||||
</a>
|
||||
<a href="https://www.codacy.com/gh/chinabugotech/hutool/dashboard?utm_source=github.com&utm_medium=referral&utm_content=chinabugotech/hutool&utm_campaign=Badge_Grade">
|
||||
<a href="https://www.codacy.com/gh/dromara/hutool/dashboard?utm_source=github.com&utm_medium=referral&utm_content=dromara/hutool&utm_campaign=Badge_Grade">
|
||||
<img alt="" src="https://app.codacy.com/project/badge/Grade/8a6897d9de7440dd9de8804c28d2871d"/>
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/cn/hutool">
|
||||
<img alt="" src="https://codecov.io/gh/chinabugotech/hutool/branch/v7-master/graph/badge.svg" />
|
||||
<a href="https://codecov.io/gh/dromara/hutool">
|
||||
<img alt="" src="https://codecov.io/gh/dromara/hutool/branch/v6-master/graph/badge.svg" />
|
||||
</a>
|
||||
<a target="_blank" href="https://gitter.im/hutool/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge">
|
||||
<img alt="" src="https://badges.gitter.im/hutool/Lobby.svg" />
|
||||
</a>
|
||||
<a target="_blank" href='https://gitee.com/chinabugotech/hutool/stargazers'>
|
||||
<img alt="star" src='https://gitee.com/chinabugotech/hutool/badge/star.svg'/>
|
||||
<a target="_blank" href='https://gitee.com/dromara/hutool/stargazers'>
|
||||
<img alt="star" src='https://gitee.com/dromara/hutool/badge/star.svg?theme=gvp'/>
|
||||
</a>
|
||||
<a target="_blank" href='https://github.com/chinabugotech/hutool'>
|
||||
<img alt="github star" src="https://img.shields.io/github/stars/chinabugotech/hutool.svg?style=social"/>
|
||||
<a target="_blank" href='https://github.com/dromara/hutool'>
|
||||
<img alt="github star" src="https://img.shields.io/github/stars/dromara/hutool.svg?style=social"/>
|
||||
</a>
|
||||
<a target="_blank" href='https://gitcode.com/chinabugotech/hutool'>
|
||||
<img src="https://gitcode.com/chinabugotech/hutool/star/badge.svg" alt="gitcode star"/>
|
||||
<a target="_blank" href='https://gitcode.com/dromara/hutool'>
|
||||
<img src="https://gitcode.com/dromara/hutool/star/badge.svg" alt="gitcode star"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -77,14 +77,13 @@
|
||||
|
||||
### ✨版本选择
|
||||
|
||||
Hutool目前主要版本4.x、5.x、6.x、7.x,选择如下:
|
||||
Hutool目前主要版本4.x、5.x、6.x,选择如下:
|
||||
|
||||
| 版本 | jdk | Maven仓库 | 主要特点 |
|
||||
|-----|--------|--------------------------------------------------------------------------------------------------------|-------------------------------------|
|
||||
| 版本 | jdk| Maven仓库 | 主要特点 |
|
||||
|-----|----|--------------------------------------------------------------------------------------------------------|-------------------------------------|
|
||||
| 4.x | jdk1.7 | [cn.hutool/hutool-all/4.x](https://mvnrepository.com/artifact/cn.hutool/hutool-all/4.6.17) | jdk1.7编译 |
|
||||
| 5.x | jdk1.8 | [cn.hutool/hutool-all/5.x ](https://mvnrepository.com/artifact/cn.hutool/hutool-all) | jdk1.8编译,使用JavaEE,适配JDK11、17、21 |
|
||||
| 6.x | jdk1.8 | [org.dromara.hutool/hutool-all/6.x ](https://mvnrepository.com/artifact/org.dromara.hutool/hutool-all) | jdk1.8编译,使用Jakarta EE,适配JDK11、17、21 |
|
||||
| 7.x | jdk17 | [cn.hutool.v7/hutool-all/6.x ](https://mvnrepository.com/artifact/org.dromara.hutool/hutool-all) | jdk17编译,使用Jakarta EE,适配17+ |
|
||||
|
||||
## 🛠️包含组件
|
||||
|
||||
@@ -129,30 +128,30 @@ Hutool目前主要版本4.x、5.x、6.x、7.x,选择如下:
|
||||
```xml
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool.v7</groupId>
|
||||
<groupId>org.dromara.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>7.0.0-M1</version>
|
||||
<version>6.0.0-M23</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### 🍐Gradle
|
||||
|
||||
```
|
||||
implementation 'cn.hutool.v7:hutool-all:7.0.0-M1'
|
||||
implementation 'org.dromara.hutool:hutool-all:6.0.0-M23'
|
||||
```
|
||||
|
||||
### 📥下载jar
|
||||
|
||||
点击以下链接,下载`hutool-all-X.X.X.jar`即可:
|
||||
|
||||
- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/v7/hutool-all/7.0.0-M1/)
|
||||
- [Maven中央库](https://repo1.maven.org/maven2/org/dromara/hutool/hutool-all/6.0.0-M23/)
|
||||
|
||||
> 🔔️注意
|
||||
> Hutool 7.x支持JDK17,对Android平台没有测试,不能保证所有工具类或工具方法可用。
|
||||
> Hutool 6.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。
|
||||
|
||||
### 🚽编译安装
|
||||
|
||||
访问Hutool的Gitee主页:[https://gitee.com/chinabugotech/hutool](https://gitee.com/chinabugotech/hutool)
|
||||
访问Hutool的Gitee主页:[https://gitee.com/dromara/hutool](https://gitee.com/dromara/hutool)
|
||||
下载整个项目源码(v6-master或v6-dev分支都可)然后进入Hutool项目目录执行:
|
||||
|
||||
```sh
|
||||
@@ -171,23 +170,23 @@ Hutool的源码分为两个分支,功能如下:
|
||||
|
||||
| 分支 | 作用 |
|
||||
|-----------|--------------------------------------------|
|
||||
| v7-master | 主分支,release版本使用的分支,与中央库提交的jar一致,不接收任何pr或修改 |
|
||||
| v7-dev | 开发分支,默认为下个版本的SNAPSHOT版本,接受修改或pr |
|
||||
| v6-master | 主分支,release版本使用的分支,与中央库提交的jar一致,不接收任何pr或修改 |
|
||||
| v6-dev | 开发分支,默认为下个版本的SNAPSHOT版本,接受修改或pr |
|
||||
|
||||
### 🐞提供bug反馈或建议
|
||||
|
||||
提交问题反馈请说明正在使用的JDK版本呢、Hutool版本和相关依赖库版本。
|
||||
|
||||
- [Gitee issue](https://gitee.com/chinabugotech/hutool/issues)
|
||||
- [Github issue](https://github.com/chinabugotech/hutool/issues)
|
||||
- [Gitcode issue Gitcode问题](https://gitcode.com/chinabugotech/hutool/issues)
|
||||
- [Gitee issue](https://gitee.com/dromara/hutool/issues)
|
||||
- [Github issue](https://github.com/dromara/hutool/issues)
|
||||
- [Gitcode issue Gitcode问题](https://gitcode.com/dromara/hutool/issues)
|
||||
|
||||
### 🧬贡献代码的步骤
|
||||
|
||||
1. 在Gitee、Github或Gitcode上fork项目到自己的repo
|
||||
2. 把fork过去的项目也就是你的项目clone到你的本地
|
||||
3. 修改代码(记得一定要修改v7-dev分支)
|
||||
4. commit后push到自己的库(v7-dev分支)
|
||||
3. 修改代码(记得一定要修改v6-dev分支)
|
||||
4. commit后push到自己的库(v6-dev分支)
|
||||
5. 登录Gitee、Github或Gitcode在你首页可以看到一个 pull request 按钮,点击它,填写一些说明信息,然后提交即可。
|
||||
6. 等待维护者合并
|
||||
|
||||
@@ -210,7 +209,7 @@ Hutool欢迎任何人为Hutool添砖加瓦,贡献代码,不过维护者是
|
||||
|
||||
## ⭐Star Hutool
|
||||
|
||||
[](https://starchart.cc/chinabugotech/hutool)
|
||||
[](https://starchart.cc/dromara/hutool)[](https://starchart.cc/dromara/hutool)
|
||||
|
||||
### GitHub Contributor Over Time
|
||||
[](https://git-contributor.com?chart=contributorOverTime&repo=dromara/hutool)
|
||||
|
||||
13
SECURITY.md
13
SECURITY.md
@@ -0,0 +1,13 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions(支持的版本)
|
||||
|
||||
| Version | Supported |
|
||||
|---------|--------------------|
|
||||
| 6.x.x | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability(报告漏洞)
|
||||
|
||||
如果你发现有安全问题或漏洞,请发送邮件到`loolly@aliyun.com`。
|
||||
|
||||
To report any found security issues or vulnerabilities, please send a mail to `loolly@aliyun.com`.
|
||||
@@ -16,12 +16,12 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
echo -e "\033[32mCheckout to v7-dev\033[0m"
|
||||
git checkout v7-dev
|
||||
echo -e "\033[32mCheckout to v6-dev\033[0m"
|
||||
git checkout v6-dev
|
||||
|
||||
echo -e "\033[32mPush to Github(origin) v7-dev\033[0m"
|
||||
git push origin v7-dev
|
||||
echo -e "\033[32mPush to Gitee v7-dev\033[0m"
|
||||
git push osc v7-dev
|
||||
echo -e "\033[32mPush to Gitcode v7-dev\033[0m"
|
||||
git push gitcode v7-dev
|
||||
echo -e "\033[32mPush to Github(origin) v6-dev\033[0m"
|
||||
git push origin v6-dev
|
||||
echo -e "\033[32mPush to Gitee v6-dev\033[0m"
|
||||
git push osc v6-dev
|
||||
echo -e "\033[32mPush to Gitcode v6-dev\033[0m"
|
||||
git push gitcode v6-dev
|
||||
|
||||
@@ -19,15 +19,15 @@
|
||||
# show Hutool logo
|
||||
"$(dirname ${BASH_SOURCE[0]})"/logo.sh
|
||||
|
||||
echo -e "\033[32mCheckout to v7-master\033[0m"
|
||||
git checkout v7-master
|
||||
echo -e "\033[32mCheckout to v6-master\033[0m"
|
||||
git checkout v6-master
|
||||
|
||||
echo -e "\033[32mMerge v7-dev branch\033[0m"
|
||||
git merge v7-dev -m 'Prepare release'
|
||||
echo -e "\033[32mMerge v6-dev branch\033[0m"
|
||||
git merge v6-dev -m 'Prepare release'
|
||||
|
||||
echo -e "\033[32mPush to Github(origin) v7-master\033[0m"
|
||||
git push origin v7-master
|
||||
echo -e "\033[32mPush to Gitee v7-master\033[0m"
|
||||
git push osc v7-master
|
||||
echo -e "\033[32mPush to Gitcode v7-master\033[0m"
|
||||
git push gitcode v7-master
|
||||
echo -e "\033[32mPush to Github(origin) v6-master\033[0m"
|
||||
git push origin v6-master
|
||||
echo -e "\033[32mPush to Gitee v6-master\033[0m"
|
||||
git push osc v6-master
|
||||
echo -e "\033[32mPush to Gitcode v6-master\033[0m"
|
||||
git push gitcode v6-master
|
||||
|
||||
10
bin/sync.sh
10
bin/sync.sh
@@ -19,10 +19,10 @@
|
||||
# show Hutool logo
|
||||
"$(dirname ${BASH_SOURCE[0]})"/logo.sh
|
||||
|
||||
# 保证当前在v7-dev分支
|
||||
git checkout v7-dev
|
||||
# 保证当前在v6-dev分支
|
||||
git checkout v6-dev
|
||||
|
||||
# 同时同步Gitee、Github和Gitcode的库
|
||||
git pull osc v7-dev
|
||||
git pull origin v7-dev
|
||||
git pull gitcode v7-dev
|
||||
git pull osc v6-dev
|
||||
git pull origin v6-dev
|
||||
git pull gitcode v6-dev
|
||||
|
||||
@@ -1 +1 @@
|
||||
7.0.0-M1
|
||||
6.0.0-M23
|
||||
|
||||
@@ -1 +1 @@
|
||||
var version = '7.0.0-M1'
|
||||
var version = '6.0.0-M23'
|
||||
@@ -7,9 +7,9 @@
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.hutool.v7</groupId>
|
||||
<groupId>org.dromara.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>7.0.0-M1</version>
|
||||
<version>6.0.0-M23</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-ai</artifactId>
|
||||
@@ -17,34 +17,34 @@
|
||||
<description>Hutool AI大模型封装</description>
|
||||
|
||||
<properties>
|
||||
<Automatic-Module-Name>cn.hutool.v7.ai</Automatic-Module-Name>
|
||||
<Automatic-Module-Name>org.dromara.hutool.ai</Automatic-Module-Name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.hutool.v7</groupId>
|
||||
<groupId>org.dromara.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool.v7</groupId>
|
||||
<groupId>org.dromara.hutool</groupId>
|
||||
<artifactId>hutool-http</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool.v7</groupId>
|
||||
<groupId>org.dromara.hutool</groupId>
|
||||
<artifactId>hutool-log</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool.v7</groupId>
|
||||
<groupId>org.dromara.hutool</groupId>
|
||||
<artifactId>hutool-json</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool.v7</groupId>
|
||||
<groupId>org.dromara.hutool</groupId>
|
||||
<artifactId>hutool-swing</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* 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.v7.ai.model.grok;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* grok支持的扩展接口
|
||||
*
|
||||
* @author elichow
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public interface GrokService extends AIService {
|
||||
|
||||
/**
|
||||
* 创建消息回复
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param maxToken 最大token
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String message(String prompt, int maxToken);
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 图片列表/或者图片Base64编码图片列表(URI形式)
|
||||
* @param detail 手动设置图片的质量,取值范围high、low、auto,默认为auto
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String chatVision(String prompt, final List<String> images, String detail);
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 传入的图片列表地址/或者图片Base64编码图片列表(URI形式)
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default String chatVision(String prompt, final List<String> images) {
|
||||
return chatVision(prompt, images, GrokCommon.GrokVision.AUTO.getDetail());
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出所有model列表
|
||||
*
|
||||
* @return model列表
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String models();
|
||||
|
||||
/**
|
||||
* 获取模型信息
|
||||
*
|
||||
* @param modelId model ID
|
||||
* @return model信息
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String getModel(String modelId);
|
||||
|
||||
/**
|
||||
* 列出所有语言model
|
||||
*
|
||||
* @return languageModel列表
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String languageModels();
|
||||
|
||||
/**
|
||||
* 获取语言模型信息
|
||||
*
|
||||
* @param modelId model ID
|
||||
* @return model信息
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String getLanguageModel(String modelId);
|
||||
|
||||
/**
|
||||
* 分词:可以将文本转换为模型可理解的 token 信息
|
||||
*
|
||||
* @param text 需要分词的内容
|
||||
* @return 分词结果
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String tokenizeText(String text);
|
||||
|
||||
/**
|
||||
* 从延迟对话中获取结果
|
||||
*
|
||||
* @param requestId 延迟对话中的延迟请求ID
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String deferredCompletion(String requestId);
|
||||
}
|
||||
@@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai;
|
||||
package org.dromara.hutool.ai;
|
||||
|
||||
import cn.hutool.v7.core.exception.HutoolException;
|
||||
import org.dromara.hutool.core.exception.HutoolException;
|
||||
|
||||
/**
|
||||
* 异常处理类
|
||||
@@ -14,16 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai;
|
||||
package org.dromara.hutool.ai;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIConfig;
|
||||
import cn.hutool.v7.ai.core.AIService;
|
||||
import cn.hutool.v7.ai.core.AIServiceProvider;
|
||||
import cn.hutool.v7.core.spi.ServiceLoader;
|
||||
import cn.hutool.v7.core.spi.SpiUtil;
|
||||
import org.dromara.hutool.ai.core.AIConfig;
|
||||
import org.dromara.hutool.ai.core.AIService;
|
||||
import org.dromara.hutool.ai.core.AIServiceProvider;
|
||||
import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* 创建AIModelService的工厂类
|
||||
@@ -33,11 +32,11 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
*/
|
||||
public class AIServiceFactory {
|
||||
|
||||
private static final Map<String, AIServiceProvider> providers = new ConcurrentHashMap<>();
|
||||
private static final Map<String, AIServiceProvider> providers = new SafeConcurrentHashMap<>();
|
||||
|
||||
// 加载所有 AIModelProvider 实现类
|
||||
static {
|
||||
final ServiceLoader<AIServiceProvider> loader = SpiUtil.loadList(AIServiceProvider.class);
|
||||
final ServiceLoader<AIServiceProvider> loader = ServiceLoader.load(AIServiceProvider.class);
|
||||
for (final AIServiceProvider provider : loader) {
|
||||
providers.put(provider.getServiceName().toLowerCase(), provider);
|
||||
}
|
||||
@@ -14,15 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai;
|
||||
package org.dromara.hutool.ai;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIConfig;
|
||||
import cn.hutool.v7.ai.core.AIService;
|
||||
import cn.hutool.v7.ai.core.Message;
|
||||
import cn.hutool.v7.ai.model.deepseek.DeepSeekService;
|
||||
import cn.hutool.v7.ai.model.doubao.DoubaoService;
|
||||
import cn.hutool.v7.ai.model.grok.GrokService;
|
||||
import cn.hutool.v7.ai.model.openai.OpenaiService;
|
||||
import org.dromara.hutool.ai.core.AIConfig;
|
||||
import org.dromara.hutool.ai.core.AIService;
|
||||
import org.dromara.hutool.ai.core.Message;
|
||||
import org.dromara.hutool.ai.model.deepseek.DeepSeekService;
|
||||
import org.dromara.hutool.ai.model.doubao.DoubaoService;
|
||||
import org.dromara.hutool.ai.model.grok.GrokService;
|
||||
import org.dromara.hutool.ai.model.hutool.HutoolService;
|
||||
import org.dromara.hutool.ai.model.openai.OpenaiService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -58,6 +59,17 @@ public class AIUtil {
|
||||
return getAIService(config, AIService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Hutool-AI服务
|
||||
*
|
||||
* @param config 创建的AI服务模型的配置
|
||||
* @return HutoolService
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static HutoolService getHutoolService(final AIConfig config) {
|
||||
return getAIService(config, HutoolService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取DeepSeek模型服务
|
||||
*
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai;
|
||||
package org.dromara.hutool.ai;
|
||||
|
||||
/**
|
||||
* 模型厂商的名称(不指具体的模型)
|
||||
@@ -23,6 +23,10 @@ package cn.hutool.v7.ai;
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public enum ModelName {
|
||||
/**
|
||||
* hutool
|
||||
*/
|
||||
HUTOOL("hutool"),
|
||||
/**
|
||||
* deepSeek
|
||||
*/
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai;
|
||||
package org.dromara.hutool.ai;
|
||||
|
||||
/**
|
||||
* 各模型厂商包含的model(指具体的模型)
|
||||
@@ -24,6 +24,21 @@ package cn.hutool.v7.ai;
|
||||
*/
|
||||
public class Models {
|
||||
|
||||
// Hutool的模型
|
||||
public enum Hutool {
|
||||
HUTOOL("hutool");
|
||||
|
||||
private final String model;
|
||||
|
||||
Hutool(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
// DeepSeek的模型
|
||||
public enum DeepSeek {
|
||||
DEEPSEEK_CHAT("deepseek-chat"),
|
||||
@@ -123,7 +138,12 @@ public class Models {
|
||||
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");
|
||||
DOUBAO_EMBEDDING_VISION("doubao-embedding-vision-241215"),
|
||||
DOUBAO_SEEDREAM_3_0_T2I("doubao-seedream-3-0-t2i-250415"),
|
||||
Doubao_Seedance_1_0_lite_t2v("doubao-seedance-1-0-lite-t2v-250428"),
|
||||
Doubao_Seedance_1_0_lite_i2v("doubao-seedance-1-0-lite-i2v-250428"),
|
||||
Wan2_1_14B_t2v("wan2-1-14b-t2v-250225"),
|
||||
Wan2_1_14B_i2v("wan2-1-14b-i2v-250225");
|
||||
|
||||
private final String model;
|
||||
|
||||
@@ -138,6 +158,23 @@ public class Models {
|
||||
|
||||
// Grok的模型
|
||||
public enum Grok {
|
||||
GROK_3_BETA_LATEST("grok-3-beta"),
|
||||
GROK_3_BETA("grok-3-beta"),
|
||||
GROK_3("grok-3-beta"),
|
||||
GROK_3_MINI_FAST_LATEST("grok-3-mini-fast-beta"),
|
||||
GROK_3_MINI_FAST_BETA("grok-3-mini-fast-beta"),
|
||||
GROK_3_MINI_FAST("grok-3-mini-fast-beta"),
|
||||
GROK_3_FAST_LATEST("grok-3-fast-beta"),
|
||||
GROK_3_FAST_BETA("grok-3-fast-beta"),
|
||||
GROK_3_FAST("grok-3-fast-beta"),
|
||||
GROK_3_MINI_LATEST("grok-3-mini-beta"),
|
||||
GROK_3_MINI_BETA("grok-3-mini-beta"),
|
||||
GROK_3_MINI("grok-3-mini-beta"),
|
||||
GROK_2_IMAGE_LATEST("grok-2-image-1212"),
|
||||
GROK_2_IMAGE("grok-2-image-1212"),
|
||||
GROK_2_IMAGE_1212("grok-2-image-1212"),
|
||||
grok_2_latest("grok-2-1212"),
|
||||
GROK_2("grok-2-1212"),
|
||||
GROK_2_1212("grok-2-1212"),
|
||||
GROK_2_VISION_1212("grok-2-vision-1212"),
|
||||
GROK_BETA("grok-beta"),
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.core;
|
||||
package org.dromara.hutool.ai.core;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -110,4 +110,36 @@ public interface AIConfig {
|
||||
*/
|
||||
Map<String, Object> getAdditionalConfigMap();
|
||||
|
||||
/**
|
||||
* 设置连接超时时间
|
||||
*
|
||||
* @param timeout 连接超时时间
|
||||
* @since 6.0.0
|
||||
*/
|
||||
void setTimeout(int timeout);
|
||||
|
||||
/**
|
||||
* 获取连接超时时间
|
||||
*
|
||||
* @return timeout
|
||||
* @since 6.0.0
|
||||
*/
|
||||
int getTimeout();
|
||||
|
||||
/**
|
||||
* 设置读取超时时间
|
||||
*
|
||||
* @param readTimeout 连接超时时间
|
||||
* @since 6.0.0
|
||||
*/
|
||||
void setReadTimeout(int readTimeout);
|
||||
|
||||
/**
|
||||
* 获取读取超时时间
|
||||
*
|
||||
* @return readTimeout
|
||||
* @since 6.0.0
|
||||
*/
|
||||
int getReadTimeout();
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.core;
|
||||
package org.dromara.hutool.ai.core;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
@@ -106,6 +106,34 @@ public class AIConfigBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置连接超时时间,不设置为默认值
|
||||
*
|
||||
* @param timeout 超时时间
|
||||
* @return config
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public synchronized AIConfigBuilder setTimout(final int timeout) {
|
||||
if (timeout > 0) {
|
||||
config.setTimeout(timeout);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置读取超时时间,不设置为默认值
|
||||
*
|
||||
* @param readTimout 取超时时间
|
||||
* @return config
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public synchronized AIConfigBuilder setReadTimout(final int readTimout) {
|
||||
if (readTimout > 0) {
|
||||
config.setReadTimeout(readTimout);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回config实例
|
||||
*
|
||||
@@ -14,13 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.core;
|
||||
package org.dromara.hutool.ai.core;
|
||||
|
||||
import cn.hutool.v7.core.spi.ServiceLoader;
|
||||
import cn.hutool.v7.core.spi.SpiUtil;
|
||||
import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* AIConfig实现类的加载器
|
||||
@@ -30,11 +29,11 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
*/
|
||||
public class AIConfigRegistry {
|
||||
|
||||
private static final Map<String, Class<? extends AIConfig>> configClasses = new ConcurrentHashMap<>();
|
||||
private static final Map<String, Class<? extends AIConfig>> configClasses = new SafeConcurrentHashMap<>();
|
||||
|
||||
// 加载所有 AIConfig 实现类
|
||||
static {
|
||||
final ServiceLoader<AIConfig> loader = SpiUtil.loadList(AIConfig.class);
|
||||
final ServiceLoader<AIConfig> loader = ServiceLoader.load(AIConfig.class);
|
||||
for (final AIConfig config : loader) {
|
||||
configClasses.put(config.getModelName().toLowerCase(), config.getClass());
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 org.dromara.hutool.ai.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 模型公共的API功能,特有的功能在model.xx.XXService下定义
|
||||
*
|
||||
* @author elichow
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public interface AIService {
|
||||
|
||||
/**
|
||||
* 对话
|
||||
*
|
||||
* @param prompt user题词
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话-SSE流式输出
|
||||
* @param prompt user题词
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default void chat(String prompt, final Consumer<String> callback){
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system", "You are a helpful assistant"));
|
||||
messages.add(new Message("user", prompt));
|
||||
chat(messages, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话
|
||||
*
|
||||
* @param messages 由目前为止的对话组成的消息列表,可以设置role,content。详细参考官方文档
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String chat(final List<Message> messages);
|
||||
|
||||
/**
|
||||
* 对话-SSE流式输出
|
||||
* @param messages 由目前为止的对话组成的消息列表,可以设置role,content。详细参考官方文档
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
void chat(final List<Message> messages, final Consumer<String> callback);
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.core;
|
||||
package org.dromara.hutool.ai.core;
|
||||
|
||||
/**
|
||||
* 用于加载AI服务,每一个通过SPI创建的AI服务都要实现此接口
|
||||
@@ -14,16 +14,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.core;
|
||||
package org.dromara.hutool.ai.core;
|
||||
|
||||
import cn.hutool.v7.ai.AIException;
|
||||
import cn.hutool.v7.http.HttpGlobalConfig;
|
||||
import cn.hutool.v7.http.HttpUtil;
|
||||
import cn.hutool.v7.http.client.Response;
|
||||
import cn.hutool.v7.http.meta.HeaderName;
|
||||
import cn.hutool.v7.http.meta.Method;
|
||||
import org.dromara.hutool.ai.AIException;
|
||||
import org.dromara.hutool.http.HttpGlobalConfig;
|
||||
import org.dromara.hutool.http.HttpUtil;
|
||||
import org.dromara.hutool.http.client.Response;
|
||||
import org.dromara.hutool.http.meta.HeaderName;
|
||||
import org.dromara.hutool.http.meta.Method;
|
||||
import org.dromara.hutool.json.JSONUtil;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 基础AIService,包含基公共参数和公共方法
|
||||
@@ -52,8 +59,8 @@ public class BaseAIService {
|
||||
protected Response sendGet(final String endpoint) {
|
||||
//链式构建请求
|
||||
try {
|
||||
//设置超时3分钟
|
||||
HttpGlobalConfig.setTimeout(180000);
|
||||
//设置超时
|
||||
HttpGlobalConfig.setTimeout(config.getTimeout());
|
||||
return HttpUtil.createRequest(config.getApiUrl() + endpoint, Method.GET)
|
||||
.header(HeaderName.ACCEPT, "application/json")
|
||||
.header(HeaderName.AUTHORIZATION, "Bearer " + config.getApiKey())
|
||||
@@ -73,7 +80,7 @@ public class BaseAIService {
|
||||
//链式构建请求
|
||||
try {
|
||||
//设置超时3分钟
|
||||
HttpGlobalConfig.setTimeout(180000);
|
||||
HttpGlobalConfig.setTimeout(config.getTimeout());
|
||||
return HttpUtil.createRequest(config.getApiUrl() + endpoint, Method.POST)
|
||||
.header(HeaderName.CONTENT_TYPE, "application/json")
|
||||
.header(HeaderName.ACCEPT, "application/json")
|
||||
@@ -96,7 +103,7 @@ public class BaseAIService {
|
||||
//链式构建请求
|
||||
try {
|
||||
//设置超时3分钟
|
||||
HttpGlobalConfig.setTimeout(180000);
|
||||
HttpGlobalConfig.setTimeout(config.getTimeout());
|
||||
return HttpUtil.createPost(config.getApiUrl() + endpoint)
|
||||
//form表单中有file对象会自动将文件编码为 multipart/form-data 格式。不需要设置
|
||||
// .header(HeaderName.CONTENT_TYPE, "multipart/form-data")
|
||||
@@ -107,4 +114,50 @@ public class BaseAIService {
|
||||
throw new AIException("Failed to send POST request:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 支持流式返回的 POST 请求
|
||||
*
|
||||
* @param endpoint 请求地址
|
||||
* @param paramMap 请求参数
|
||||
* @param callback 流式数据回调函数
|
||||
*/
|
||||
protected void sendPostStream(final String endpoint, final Map<String, Object> paramMap, Consumer<String> callback) {
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
// 创建连接
|
||||
URL apiUrl = new URL(config.getApiUrl() + endpoint);
|
||||
connection = (HttpURLConnection) apiUrl.openConnection();
|
||||
connection.setRequestMethod(Method.POST.name());
|
||||
connection.setRequestProperty(HeaderName.CONTENT_TYPE.getValue(), "application/json");
|
||||
connection.setRequestProperty(HeaderName.AUTHORIZATION.getValue(), "Bearer " + config.getApiKey());
|
||||
connection.setDoOutput(true);
|
||||
//设置读取超时
|
||||
connection.setReadTimeout(config.getReadTimeout());
|
||||
//设置连接超时
|
||||
connection.setConnectTimeout(config.getTimeout());
|
||||
// 发送请求体
|
||||
try (OutputStream os = connection.getOutputStream()) {
|
||||
String jsonInputString = JSONUtil.toJsonStr(paramMap);
|
||||
os.write(jsonInputString.getBytes());
|
||||
os.flush();
|
||||
}
|
||||
|
||||
// 读取流式响应
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
// 调用回调函数处理每一行数据
|
||||
callback.accept(line);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
callback.accept("{\"error\": \"" + e.getMessage() + "\"}");
|
||||
} finally {
|
||||
// 关闭连接
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.core;
|
||||
package org.dromara.hutool.ai.core;
|
||||
|
||||
import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Config基础类,定义模型配置的基本属性
|
||||
@@ -34,7 +35,11 @@ public class BaseConfig implements AIConfig {
|
||||
//具体模型
|
||||
protected volatile String model;
|
||||
//动态扩展字段
|
||||
protected Map<String, Object> additionalConfig = new ConcurrentHashMap<>();
|
||||
protected Map<String, Object> additionalConfig = new SafeConcurrentHashMap<>();
|
||||
//连接超时时间
|
||||
protected volatile int timeout = 180000;
|
||||
//读取超时时间
|
||||
protected volatile int readTimeout = 300000;
|
||||
|
||||
@Override
|
||||
public void setApiKey(final String apiKey) {
|
||||
@@ -78,7 +83,26 @@ public class BaseConfig implements AIConfig {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAdditionalConfigMap() {
|
||||
return new ConcurrentHashMap<>(additionalConfig);
|
||||
return new SafeConcurrentHashMap<>(additionalConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimeout(final int timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReadTimeout() {
|
||||
return readTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadTimeout(final int readTimeout) {
|
||||
this.readTimeout = readTimeout;
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.core;
|
||||
package org.dromara.hutool.ai.core;
|
||||
|
||||
/**
|
||||
* 公共Message类
|
||||
@@ -21,4 +21,4 @@
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.core;
|
||||
package org.dromara.hutool.ai.core;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.deepseek;
|
||||
package org.dromara.hutool.ai.model.deepseek;
|
||||
|
||||
/**
|
||||
* deepSeek公共类
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.deepseek;
|
||||
package org.dromara.hutool.ai.model.deepseek;
|
||||
|
||||
import cn.hutool.v7.ai.Models;
|
||||
import cn.hutool.v7.ai.core.BaseConfig;
|
||||
import org.dromara.hutool.ai.Models;
|
||||
import org.dromara.hutool.ai.core.BaseConfig;
|
||||
|
||||
/**
|
||||
* DeepSeek配置类,初始化API接口地址,设置默认的模型
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.deepseek;
|
||||
package org.dromara.hutool.ai.model.deepseek;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIConfig;
|
||||
import cn.hutool.v7.ai.core.AIServiceProvider;
|
||||
import org.dromara.hutool.ai.core.AIConfig;
|
||||
import org.dromara.hutool.ai.core.AIServiceProvider;
|
||||
|
||||
/**
|
||||
* 创建DeepSeek服务实现类
|
||||
@@ -14,9 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.deepseek;
|
||||
package org.dromara.hutool.ai.model.deepseek;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIService;
|
||||
import org.dromara.hutool.ai.core.AIService;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* deepSeek支持的扩展接口
|
||||
@@ -35,6 +37,14 @@ public interface DeepSeekService extends AIService {
|
||||
*/
|
||||
String beta(String prompt);
|
||||
|
||||
/**
|
||||
* 模型beta功能-SSE流式输出
|
||||
* @param prompt 题词
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
void beta(String prompt, final Consumer<String> callback);
|
||||
|
||||
/**
|
||||
* 列出所有模型列表
|
||||
*
|
||||
@@ -14,18 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.deepseek;
|
||||
package org.dromara.hutool.ai.model.deepseek;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIConfig;
|
||||
import cn.hutool.v7.ai.core.BaseAIService;
|
||||
import cn.hutool.v7.ai.core.Message;
|
||||
import cn.hutool.v7.http.client.Response;
|
||||
import cn.hutool.v7.json.JSONUtil;
|
||||
import org.dromara.hutool.ai.core.AIConfig;
|
||||
import org.dromara.hutool.ai.core.BaseAIService;
|
||||
import org.dromara.hutool.ai.core.Message;
|
||||
import org.dromara.hutool.core.thread.ThreadUtil;
|
||||
import org.dromara.hutool.http.client.Response;
|
||||
import org.dromara.hutool.json.JSONUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* DeepSeek服务,AI具体功能的实现
|
||||
@@ -54,15 +55,6 @@ public class DeepSeekServiceImpl extends BaseAIService implements DeepSeekServic
|
||||
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);
|
||||
@@ -70,6 +62,12 @@ public class DeepSeekServiceImpl extends BaseAIService implements DeepSeekServic
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chat(List<Message> messages, Consumer<String> callback) {
|
||||
Map<String, Object> paramMap = buildChatStreamRequestBody(messages);
|
||||
ThreadUtil.newThread(() -> sendPostStream(CHAT_ENDPOINT, paramMap, callback::accept), "deepseek-chat-sse").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String beta(final String prompt) {
|
||||
final String paramJson = buildBetaRequestBody(prompt);
|
||||
@@ -77,6 +75,12 @@ public class DeepSeekServiceImpl extends BaseAIService implements DeepSeekServic
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beta(String prompt, Consumer<String> callback) {
|
||||
Map<String, Object> paramMap = buildBetaStreamRequestBody(prompt);
|
||||
ThreadUtil.newThread(() -> sendPostStream(BETA_ENDPOINT, paramMap, callback::accept), "deepseek-beta-sse").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String models() {
|
||||
final Response response = sendGet(MODELS_ENDPOINT);
|
||||
@@ -101,6 +105,19 @@ public class DeepSeekServiceImpl extends BaseAIService implements DeepSeekServic
|
||||
return JSONUtil.toJsonStr(paramMap);
|
||||
}
|
||||
|
||||
// 构建chatStream请求体
|
||||
private Map<String, Object> buildChatStreamRequestBody(final List<Message> messages) {
|
||||
//使用JSON工具
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("stream", true);
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("messages", messages);
|
||||
//合并其他参数
|
||||
paramMap.putAll(config.getAdditionalConfigMap());
|
||||
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
// 构建beta请求体
|
||||
private String buildBetaRequestBody(final String prompt) {
|
||||
// 定义消息结构
|
||||
@@ -114,4 +131,17 @@ public class DeepSeekServiceImpl extends BaseAIService implements DeepSeekServic
|
||||
return JSONUtil.toJsonStr(paramMap);
|
||||
}
|
||||
|
||||
// 构建betaStream请求体
|
||||
private Map<String, Object> buildBetaStreamRequestBody(final String prompt) {
|
||||
//使用JSON工具
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("stream", true);
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("prompt", prompt);
|
||||
//合并其他参数
|
||||
paramMap.putAll(config.getAdditionalConfigMap());
|
||||
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,4 +21,4 @@
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.deepseek;
|
||||
package org.dromara.hutool.ai.model.deepseek;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.doubao;
|
||||
package org.dromara.hutool.ai.model.doubao;
|
||||
|
||||
/**
|
||||
* doubao公共类
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.doubao;
|
||||
package org.dromara.hutool.ai.model.doubao;
|
||||
|
||||
import cn.hutool.v7.ai.Models;
|
||||
import cn.hutool.v7.ai.core.BaseConfig;
|
||||
import org.dromara.hutool.ai.Models;
|
||||
import org.dromara.hutool.ai.core.BaseConfig;
|
||||
|
||||
/**
|
||||
* Doubao配置类,初始化API接口地址,设置默认的模型
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.doubao;
|
||||
package org.dromara.hutool.ai.model.doubao;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIConfig;
|
||||
import cn.hutool.v7.ai.core.AIServiceProvider;
|
||||
import org.dromara.hutool.ai.core.AIConfig;
|
||||
import org.dromara.hutool.ai.core.AIServiceProvider;
|
||||
|
||||
/**
|
||||
* 创建Doubap服务实现类
|
||||
@@ -14,12 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.doubao;
|
||||
package org.dromara.hutool.ai.model.doubao;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIService;
|
||||
import cn.hutool.v7.ai.core.Message;
|
||||
import org.dromara.hutool.ai.core.AIService;
|
||||
import org.dromara.hutool.ai.core.Message;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* doubao支持的扩展接口
|
||||
@@ -29,17 +31,6 @@ import java.util.List;
|
||||
*/
|
||||
public interface DoubaoService extends AIService {
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 提问
|
||||
* @param images 图片列表/或者图片Base64编码图片列表(URI形式)
|
||||
* @param detail 手动设置图片的质量,取值范围high、low、auto,默认为auto
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String chatVision(String prompt, final List<String> images, String detail);
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
@@ -52,9 +43,43 @@ public interface DoubaoService extends AIService {
|
||||
return chatVision(prompt, images, DoubaoCommon.DoubaoVision.AUTO.getDetail());
|
||||
}
|
||||
|
||||
/**
|
||||
* 图像理解-SSE流式输出
|
||||
*
|
||||
* @param prompt 提问
|
||||
* @param images 图片列表/或者图片Base64编码图片列表(URI形式)
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default void chatVision(String prompt, final List<String> images, final Consumer<String> callback) {
|
||||
chatVision(prompt, images, DoubaoCommon.DoubaoVision.AUTO.getDetail(), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 提问
|
||||
* @param images 图片列表/或者图片Base64编码图片列表(URI形式)
|
||||
* @param detail 手动设置图片的质量,取值范围high、low、auto,默认为auto
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String chatVision(String prompt, final List<String> images, String detail);
|
||||
|
||||
/**
|
||||
* 图像理解-SSE流式输出
|
||||
*
|
||||
* @param prompt 提问
|
||||
* @param images 传入的图片列表地址/或者图片Base64编码图片列表(URI形式)
|
||||
* @param detail 手动设置图片的质量,取值范围high、low、auto,默认为auto
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
void chatVision(String prompt, final List<String> images, String detail, final Consumer<String> callback);
|
||||
|
||||
/**
|
||||
* 创建视频生成任务
|
||||
* 注意:调用该方法时,配置config中的model为您创建的推理接入点(Endpoint)ID。详细参考官方文档
|
||||
* 注意:调用该方法时,配置config中的model为生成视频的模型或者您创建的推理接入点(Endpoint)ID。详细参考官方文档
|
||||
*
|
||||
* @param text 文本提示词
|
||||
* @param image 图片/或者图片Base64编码图片(URI形式)
|
||||
@@ -114,6 +139,15 @@ public interface DoubaoService extends AIService {
|
||||
*/
|
||||
String botsChat(final List<Message> messages);
|
||||
|
||||
/**
|
||||
* 应用(Bot)-SSE流式输出 config中model设置为您创建的应用ID
|
||||
*
|
||||
* @param messages 由对话组成的消息列表。如系统人设,背景信息等,用户自定义的信息
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
void botsChat(final List<Message> messages, final Consumer<String> callback);
|
||||
|
||||
/**
|
||||
* 分词:可以将文本转换为模型可理解的 token id,并返回文本的 tokens 数量、token id、 token 在原始文本中的偏移量等信息
|
||||
*
|
||||
@@ -132,7 +166,12 @@ public interface DoubaoService extends AIService {
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String batchChat(String prompt);
|
||||
default 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量推理 Chat
|
||||
@@ -179,7 +218,26 @@ public interface DoubaoService extends AIService {
|
||||
* @return AI的回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String chatContext(String prompt, String contextId);
|
||||
default String chatContext(String prompt, String contextId){
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("user", prompt));
|
||||
return chatContext(messages, contextId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上下文缓存对话-SSE流式输出
|
||||
* 注意:配置config中的model可以为您创建的推理接入点(Endpoint)ID,也可以是支持chat的model
|
||||
*
|
||||
* @param prompt 对话的内容题词
|
||||
* @param contextId 创建上下文缓存后获取的缓存id
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default void chatContext(String prompt, String contextId, final Consumer<String> callback){
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("user", prompt));
|
||||
chatContext(messages, contextId, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上下文缓存对话: 向大模型发起带上下文缓存的请求
|
||||
@@ -192,4 +250,24 @@ public interface DoubaoService extends AIService {
|
||||
*/
|
||||
String chatContext(final List<Message> messages, String contextId);
|
||||
|
||||
/**
|
||||
* 上下文缓存对话-SSE流式输出
|
||||
* 注意:配置config中的model可以为您创建的推理接入点(Endpoint)ID,也可以是支持chat的model
|
||||
*
|
||||
* @param messages 对话的信息 不支持最后一个元素的role设置为assistant。如使用session 缓存(mode设置为session)传入最新一轮对话的信息,无需传入历史信息
|
||||
* @param contextId 创建上下文缓存后获取的缓存id
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
void chatContext(final List<Message> messages, String contextId, final Consumer<String> callback);
|
||||
|
||||
/**
|
||||
* 文生图
|
||||
* 请设置config中model为支持图片功能的模型,目前支持Doubao-Seedream-3.0-t2i
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @return 包含生成图片的url
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String imagesGenerations(String prompt);
|
||||
}
|
||||
@@ -14,19 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.doubao;
|
||||
package org.dromara.hutool.ai.model.doubao;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIConfig;
|
||||
import cn.hutool.v7.ai.core.BaseAIService;
|
||||
import cn.hutool.v7.ai.core.Message;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import cn.hutool.v7.http.client.Response;
|
||||
import cn.hutool.v7.json.JSONUtil;
|
||||
import org.dromara.hutool.ai.core.AIConfig;
|
||||
import org.dromara.hutool.ai.core.BaseAIService;
|
||||
import org.dromara.hutool.ai.core.Message;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.thread.ThreadUtil;
|
||||
import org.dromara.hutool.http.client.Response;
|
||||
import org.dromara.hutool.json.JSONUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Doubao服务,AI具体功能的实现
|
||||
@@ -54,21 +56,14 @@ public class DoubaoServiceImpl extends BaseAIService implements DoubaoService {
|
||||
private final String CHAT_CONTEXT = "/context/chat/completions";
|
||||
//创建视频生成任务
|
||||
private final String CREATE_VIDEO = "/contents/generations/tasks";
|
||||
//文生图
|
||||
private final String IMAGES_GENERATIONS = "/images/generations";
|
||||
|
||||
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);
|
||||
@@ -76,6 +71,12 @@ public class DoubaoServiceImpl extends BaseAIService implements DoubaoService {
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chat(List<Message> messages, Consumer<String> callback) {
|
||||
Map<String, Object> paramMap = buildChatStreamRequestBody(messages);
|
||||
ThreadUtil.newThread(() -> sendPostStream(CHAT_ENDPOINT, paramMap, callback::accept), "doubao-chat-sse").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chatVision(String prompt, final List<String> images, String detail) {
|
||||
String paramJson = buildChatVisionRequestBody(prompt, images, detail);
|
||||
@@ -83,6 +84,12 @@ public class DoubaoServiceImpl extends BaseAIService implements DoubaoService {
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chatVision(String prompt, List<String> images, String detail, Consumer<String> callback) {
|
||||
Map<String, Object> paramMap = buildChatVisionStreamRequestBody(prompt, images, detail);
|
||||
ThreadUtil.newThread(() -> sendPostStream(CHAT_ENDPOINT, paramMap, callback::accept), "doubao-chatVision-sse").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String videoTasks(String text, String image, final List<DoubaoCommon.DoubaoVideo> videoParams) {
|
||||
String paramJson = buildGenerationsTasksRequestBody(text, image, videoParams);
|
||||
@@ -118,6 +125,12 @@ public class DoubaoServiceImpl extends BaseAIService implements DoubaoService {
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void botsChat(List<Message> messages, Consumer<String> callback) {
|
||||
Map<String, Object> paramMap = buildBotsChatStreamRequestBody(messages);
|
||||
ThreadUtil.newThread(() -> sendPostStream(BOTS_CHAT, paramMap, callback::accept), "doubao-botsChat-sse").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tokenization(String[] text) {
|
||||
String paramJson = buildTokenizationRequestBody(text);
|
||||
@@ -125,15 +138,6 @@ public class DoubaoServiceImpl extends BaseAIService implements DoubaoService {
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@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);
|
||||
@@ -148,14 +152,6 @@ public class DoubaoServiceImpl extends BaseAIService implements DoubaoService {
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@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);
|
||||
@@ -163,6 +159,19 @@ public class DoubaoServiceImpl extends BaseAIService implements DoubaoService {
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chatContext(List<Message> messages, String contextId, Consumer<String> callback) {
|
||||
Map<String, Object> paramMap = buildChatContentStreamRequestBody(messages, contextId);
|
||||
ThreadUtil.newThread(() -> sendPostStream(CHAT_CONTEXT, paramMap, callback::accept), "doubao-chatContext-sse").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String imagesGenerations(String prompt) {
|
||||
String paramJson = buildImagesGenerationsRequestBody(prompt);
|
||||
Response response = sendPost(IMAGES_GENERATIONS, paramJson);
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
// 构建chat请求体
|
||||
private String buildChatRequestBody(final List<Message> messages) {
|
||||
//使用JSON工具
|
||||
@@ -175,6 +184,19 @@ public class DoubaoServiceImpl extends BaseAIService implements DoubaoService {
|
||||
return JSONUtil.toJsonStr(paramMap);
|
||||
}
|
||||
|
||||
// 构建chatStream请求体
|
||||
private Map<String, Object> buildChatStreamRequestBody(final List<Message> messages) {
|
||||
//使用JSON工具
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("stream", true);
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("messages", messages);
|
||||
//合并其他参数
|
||||
paramMap.putAll(config.getAdditionalConfigMap());
|
||||
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
//构建chatVision请求体
|
||||
private String buildChatVisionRequestBody(String prompt, final List<String> images, String detail) {
|
||||
// 定义消息结构
|
||||
@@ -206,6 +228,37 @@ public class DoubaoServiceImpl extends BaseAIService implements DoubaoService {
|
||||
return JSONUtil.toJsonStr(paramMap);
|
||||
}
|
||||
|
||||
private Map<String, Object> buildChatVisionStreamRequestBody(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("stream", true);
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("messages", messages);
|
||||
//合并其他参数
|
||||
paramMap.putAll(config.getAdditionalConfigMap());
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
//构建文本向量化请求体
|
||||
private String buildEmbeddingTextRequestBody(String[] input) {
|
||||
//使用JSON工具
|
||||
@@ -253,6 +306,10 @@ public class DoubaoServiceImpl extends BaseAIService implements DoubaoService {
|
||||
return buildChatRequestBody(messages);
|
||||
}
|
||||
|
||||
private Map<String, Object> buildBotsChatStreamRequestBody(final List<Message> messages) {
|
||||
return buildChatStreamRequestBody(messages);
|
||||
}
|
||||
|
||||
//构建分词请求体
|
||||
private String buildTokenizationRequestBody(String[] text) {
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
@@ -266,6 +323,10 @@ public class DoubaoServiceImpl extends BaseAIService implements DoubaoService {
|
||||
return buildChatRequestBody(messages);
|
||||
}
|
||||
|
||||
private Map<String, Object> buildBatchChatStreamRequestBody(final List<Message> messages) {
|
||||
return buildChatStreamRequestBody(messages);
|
||||
}
|
||||
|
||||
//构建创建上下文缓存请求体
|
||||
private String buildCreateContextRequest(final List<Message> messages, String mode) {
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
@@ -291,6 +352,19 @@ public class DoubaoServiceImpl extends BaseAIService implements DoubaoService {
|
||||
return JSONUtil.toJsonStr(paramMap);
|
||||
}
|
||||
|
||||
private Map<String, Object> buildChatContentStreamRequestBody(final List<Message> messages, String contextId) {
|
||||
//使用JSON工具
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("stream", true);
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("messages", messages);
|
||||
paramMap.put("context_id", contextId);
|
||||
//合并其他参数
|
||||
paramMap.putAll(config.getAdditionalConfigMap());
|
||||
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
//构建创建视频任务请求体
|
||||
private String buildGenerationsTasksRequestBody(String text, String image, final List<DoubaoCommon.DoubaoVideo> videoParams) {
|
||||
//使用JSON工具
|
||||
@@ -306,7 +380,7 @@ public class DoubaoServiceImpl extends BaseAIService implements DoubaoService {
|
||||
content.add(textMap);
|
||||
}
|
||||
//添加图片参数
|
||||
if (!StrUtil.isNotBlank(image)) {
|
||||
if (!StrUtil.isBlank(image)) {
|
||||
final Map<String, Object> imgUrlMap = new HashMap<>();
|
||||
imgUrlMap.put("type", "image_url");
|
||||
final Map<String, String> urlMap = new HashMap<>();
|
||||
@@ -351,4 +425,15 @@ public class DoubaoServiceImpl extends BaseAIService implements DoubaoService {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,4 +21,4 @@
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.doubao;
|
||||
package org.dromara.hutool.ai.model.doubao;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.grok;
|
||||
package org.dromara.hutool.ai.model.grok;
|
||||
|
||||
/**
|
||||
* grok公共类
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.grok;
|
||||
package org.dromara.hutool.ai.model.grok;
|
||||
|
||||
import cn.hutool.v7.ai.Models;
|
||||
import cn.hutool.v7.ai.core.BaseConfig;
|
||||
import org.dromara.hutool.ai.Models;
|
||||
import org.dromara.hutool.ai.core.BaseConfig;
|
||||
|
||||
/**
|
||||
* Grok配置类,初始化API接口地址,设置默认的模型
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.grok;
|
||||
package org.dromara.hutool.ai.model.grok;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIConfig;
|
||||
import cn.hutool.v7.ai.core.AIServiceProvider;
|
||||
import org.dromara.hutool.ai.core.AIConfig;
|
||||
import org.dromara.hutool.ai.core.AIServiceProvider;
|
||||
|
||||
/**
|
||||
* 创建Grok服务实现类
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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 org.dromara.hutool.ai.model.grok;
|
||||
|
||||
import org.dromara.hutool.ai.core.AIService;
|
||||
import org.dromara.hutool.ai.core.Message;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* grok支持的扩展接口
|
||||
*
|
||||
* @author elichow
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public interface GrokService extends AIService {
|
||||
|
||||
/**
|
||||
* 创建消息回复
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param maxToken 最大token
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default 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));
|
||||
return message(messages, maxToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建消息回复-SSE流式输出
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param maxToken 最大token
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default void message(String prompt, int maxToken, final Consumer<String> callback){
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system", "You are a helpful assistant"));
|
||||
messages.add(new Message("user", prompt));
|
||||
message(messages, maxToken, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建消息回复
|
||||
*
|
||||
* @param messages messages 由对话组成的消息列表。如系统人设,背景信息等,用户自定义的信息
|
||||
* @param maxToken 最大token
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String message(List<Message> messages, int maxToken);
|
||||
|
||||
/**
|
||||
* 创建消息回复-SSE流式输出
|
||||
*
|
||||
* @param messages messages 由对话组成的消息列表。如系统人设,背景信息等,用户自定义的信息
|
||||
* @param maxToken 最大token
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
void message(List<Message> messages, int maxToken, final Consumer<String> callback);
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 图片列表/或者图片Base64编码图片列表(URI形式)
|
||||
* @param detail 手动设置图片的质量,取值范围high、low、auto,默认为auto
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String chatVision(String prompt, final List<String> images, String detail);
|
||||
|
||||
/**
|
||||
* 图像理解-SSE流式输出
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 图片列表/或者图片Base64编码图片列表(URI形式)
|
||||
* @param detail 手动设置图片的质量,取值范围high、low、auto,默认为auto
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
void chatVision(String prompt, final List<String> images, String detail,final Consumer<String> callback);
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 传入的图片列表地址/或者图片Base64编码图片列表(URI形式)
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default String chatVision(String prompt, final List<String> images) {
|
||||
return chatVision(prompt, images, GrokCommon.GrokVision.AUTO.getDetail());
|
||||
}
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 传入|的图片列表地址/或者图片Base64编码图片列表(URI形式)
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default void chatVision(String prompt, final List<String> images, final Consumer<String> callback){
|
||||
chatVision(prompt, images, GrokCommon.GrokVision.AUTO.getDetail(), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出所有model列表
|
||||
*
|
||||
* @return model列表
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String models();
|
||||
|
||||
/**
|
||||
* 获取模型信息
|
||||
*
|
||||
* @param modelId model ID
|
||||
* @return model信息
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String getModel(String modelId);
|
||||
|
||||
/**
|
||||
* 列出所有语言model
|
||||
*
|
||||
* @return languageModel列表
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String languageModels();
|
||||
|
||||
/**
|
||||
* 获取语言模型信息
|
||||
*
|
||||
* @param modelId model ID
|
||||
* @return model信息
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String getLanguageModel(String modelId);
|
||||
|
||||
/**
|
||||
* 分词:可以将文本转换为模型可理解的 token 信息
|
||||
*
|
||||
* @param text 需要分词的内容
|
||||
* @return 分词结果
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String tokenizeText(String text);
|
||||
|
||||
/**
|
||||
* 从延迟对话中获取结果
|
||||
*
|
||||
* @param requestId 延迟对话中的延迟请求ID
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String deferredCompletion(String requestId);
|
||||
|
||||
/**
|
||||
* 文生图
|
||||
* 请设置config中model为支持图片功能的模型,目前支持GROK_2_IMAGE
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @return 包含生成图片的url
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String imagesGenerations(String prompt);
|
||||
}
|
||||
@@ -14,18 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.grok;
|
||||
package org.dromara.hutool.ai.model.grok;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIConfig;
|
||||
import cn.hutool.v7.ai.core.BaseAIService;
|
||||
import cn.hutool.v7.ai.core.Message;
|
||||
import cn.hutool.v7.http.client.Response;
|
||||
import cn.hutool.v7.json.JSONUtil;
|
||||
import org.dromara.hutool.ai.core.AIConfig;
|
||||
import org.dromara.hutool.ai.core.BaseAIService;
|
||||
import org.dromara.hutool.ai.core.Message;
|
||||
import org.dromara.hutool.core.thread.ThreadUtil;
|
||||
import org.dromara.hutool.http.client.Response;
|
||||
import org.dromara.hutool.json.JSONUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Grok服务,AI具体功能的实现
|
||||
@@ -47,21 +49,14 @@ public class GrokServiceImpl extends BaseAIService implements GrokService {
|
||||
private final String TOKENIZE_TEXT = "/tokenize-text";
|
||||
//获取延迟对话
|
||||
private final String DEFERRED_COMPLETION = "/chat/deferred-completion";
|
||||
//文生图
|
||||
private final String IMAGES_GENERATIONS = "/images/generations";
|
||||
|
||||
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);
|
||||
@@ -70,16 +65,24 @@ public class GrokServiceImpl extends BaseAIService implements GrokService {
|
||||
}
|
||||
|
||||
@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));
|
||||
public void chat(List<Message> messages, Consumer<String> callback) {
|
||||
Map<String, Object> paramMap = buildChatStreamRequestBody(messages);
|
||||
ThreadUtil.newThread(() -> sendPostStream(CHAT_ENDPOINT, paramMap, callback::accept), "grok-chat-sse").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String message(final List<Message> messages, int maxToken) {
|
||||
String paramJson = buildMessageRequestBody(messages, maxToken);
|
||||
Response response = sendPost(MESSAGES, paramJson);
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void message(List<Message> messages, int maxToken, final Consumer<String> callback) {
|
||||
Map<String, Object> paramMap = buildMessageStreamRequestBody(messages, maxToken);
|
||||
ThreadUtil.newThread(() -> sendPostStream(MESSAGES, paramMap, callback::accept), "grok-message-sse").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chatVision(String prompt, final List<String> images, String detail) {
|
||||
String paramJson = buildChatVisionRequestBody(prompt, images, detail);
|
||||
@@ -87,6 +90,12 @@ public class GrokServiceImpl extends BaseAIService implements GrokService {
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chatVision(String prompt, List<String> images, String detail, Consumer<String> callback) {
|
||||
Map<String, Object> paramMap = buildChatVisionStreamRequestBody(prompt, images, detail);
|
||||
ThreadUtil.newThread(() -> sendPostStream(CHAT_ENDPOINT, paramMap, callback::accept), "grok-chatVision-sse").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String models() {
|
||||
Response response = sendGet(MODELS_ENDPOINT);
|
||||
@@ -124,6 +133,13 @@ public class GrokServiceImpl extends BaseAIService implements GrokService {
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String imagesGenerations(String prompt) {
|
||||
String paramJson = buildImagesGenerationsRequestBody(prompt);
|
||||
Response response = sendPost(IMAGES_GENERATIONS, paramJson);
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
// 构建chat请求体
|
||||
private String buildChatRequestBody(final List<Message> messages) {
|
||||
//使用JSON工具
|
||||
@@ -136,6 +152,18 @@ public class GrokServiceImpl extends BaseAIService implements GrokService {
|
||||
return JSONUtil.toJsonStr(paramMap);
|
||||
}
|
||||
|
||||
private Map<String, Object> buildChatStreamRequestBody(final List<Message> messages) {
|
||||
//使用JSON工具
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("stream", true);
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("messages", messages);
|
||||
//合并其他参数
|
||||
paramMap.putAll(config.getAdditionalConfigMap());
|
||||
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
//构建chatVision请求体
|
||||
private String buildChatVisionRequestBody(String prompt, final List<String> images, String detail) {
|
||||
// 定义消息结构
|
||||
@@ -167,6 +195,37 @@ public class GrokServiceImpl extends BaseAIService implements GrokService {
|
||||
return JSONUtil.toJsonStr(paramMap);
|
||||
}
|
||||
|
||||
private Map<String, Object> buildChatVisionStreamRequestBody(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("stream", true);
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("messages", messages);
|
||||
//合并其他参数
|
||||
paramMap.putAll(config.getAdditionalConfigMap());
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
//构建消息回复请求体
|
||||
private String buildMessageRequestBody(final List<Message> messages, int maxToken) {
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
@@ -179,6 +238,18 @@ public class GrokServiceImpl extends BaseAIService implements GrokService {
|
||||
return JSONUtil.toJsonStr(paramMap);
|
||||
}
|
||||
|
||||
private Map<String, Object> buildMessageStreamRequestBody(final List<Message> messages, int maxToken) {
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("stream", true);
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("messages", messages);
|
||||
paramMap.put("max_tokens", maxToken);
|
||||
//合并其他参数
|
||||
paramMap.putAll(config.getAdditionalConfigMap());
|
||||
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
//构建分词请求体
|
||||
private String buildTokenizeRequestBody(String text) {
|
||||
//使用JSON工具
|
||||
@@ -190,4 +261,15 @@ public class GrokServiceImpl extends BaseAIService implements GrokService {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -21,4 +21,4 @@
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.grok;
|
||||
package org.dromara.hutool.ai.model.grok;
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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 org.dromara.hutool.ai.model.hutool;
|
||||
|
||||
/**
|
||||
* hutool公共类
|
||||
*
|
||||
* @author elichow
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class HutoolCommon {
|
||||
|
||||
//hutool视觉参数
|
||||
public enum HutoolVision {
|
||||
|
||||
AUTO("auto"),
|
||||
LOW("low"),
|
||||
HIGH("high");
|
||||
|
||||
private final String detail;
|
||||
|
||||
HutoolVision(String detail) {
|
||||
this.detail = detail;
|
||||
}
|
||||
|
||||
public String getDetail() {
|
||||
return detail;
|
||||
}
|
||||
}
|
||||
|
||||
//hutool音频参数
|
||||
public enum HutoolSpeech {
|
||||
|
||||
ALLOY("alloy"),
|
||||
ASH("ash"),
|
||||
CORAL("coral"),
|
||||
ECHO("echo"),
|
||||
FABLE("fable"),
|
||||
ONYX("onyx"),
|
||||
NOVA("nova"),
|
||||
SAGE("sage"),
|
||||
SHIMMER("shimmer");
|
||||
|
||||
private final String voice;
|
||||
|
||||
HutoolSpeech(String voice) {
|
||||
this.voice = voice;
|
||||
}
|
||||
|
||||
public String getVoice() {
|
||||
return voice;
|
||||
}
|
||||
}
|
||||
|
||||
//hutool视频生成参数
|
||||
public enum HutoolVideo {
|
||||
|
||||
//宽高比例
|
||||
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;
|
||||
|
||||
HutoolVideo(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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,34 +14,36 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.core;
|
||||
package org.dromara.hutool.ai.model.hutool;
|
||||
|
||||
import java.util.List;
|
||||
import org.dromara.hutool.ai.Models;
|
||||
import org.dromara.hutool.ai.core.BaseConfig;
|
||||
|
||||
/**
|
||||
* 模型公共的API功能,特有的功能在model.xx.XXService下定义
|
||||
* Hutool配置类,初始化API接口地址,设置默认的模型
|
||||
*
|
||||
* @author elichow
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public interface AIService {
|
||||
public class HutoolConfig extends BaseConfig {
|
||||
|
||||
/**
|
||||
* 对话
|
||||
*
|
||||
* @param prompt user题词
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String chat(String prompt);
|
||||
private final String API_URL = "https://api.hutool.cn/ai/api";
|
||||
|
||||
/**
|
||||
* 对话
|
||||
*
|
||||
* @param messages 由目前为止的对话组成的消息列表,可以设置role,content。详细参考官方文档
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String chat(final List<Message> messages);
|
||||
private final String DEFAULT_MODEL = Models.Hutool.HUTOOL.getModel();
|
||||
|
||||
public HutoolConfig() {
|
||||
setApiUrl(API_URL);
|
||||
setModel(DEFAULT_MODEL);
|
||||
}
|
||||
|
||||
public HutoolConfig(String apiKey) {
|
||||
this();
|
||||
setApiKey(apiKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModelName() {
|
||||
return "hutool";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 org.dromara.hutool.ai.model.hutool;
|
||||
|
||||
import org.dromara.hutool.ai.core.AIConfig;
|
||||
import org.dromara.hutool.ai.core.AIServiceProvider;
|
||||
|
||||
/**r
|
||||
* 创建Hutool服务实现类
|
||||
*
|
||||
* @author elichow
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class HutoolProvider implements AIServiceProvider {
|
||||
|
||||
@Override
|
||||
public String getServiceName() {
|
||||
return "hutool";
|
||||
}
|
||||
|
||||
@Override
|
||||
public HutoolService create(final AIConfig config) {
|
||||
return new HutoolServiceImpl(config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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 org.dromara.hutool.ai.model.hutool;
|
||||
|
||||
import org.dromara.hutool.ai.core.AIService;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* hutool支持的扩展接口
|
||||
*
|
||||
* @author elichow
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public interface HutoolService extends AIService {
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 图片列表/或者图片Base64编码图片列表(URI形式)
|
||||
* @param detail 手动设置图片的质量,取值范围high、low、auto,默认为auto
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String chatVision(String prompt, final List<String> images, String detail);
|
||||
|
||||
/**
|
||||
* 图像理解-SSE流式输出
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 图片列表/或者图片Base64编码图片列表(URI形式)
|
||||
* @param detail 手动设置图片的质量,取值范围high、low、auto,默认为auto
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
void chatVision(String prompt, final List<String> images, String detail,final Consumer<String> callback);
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 传入的图片列表地址/或者图片Base64编码图片列表(URI形式)
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default String chatVision(String prompt, final List<String> images) {
|
||||
return chatVision(prompt, images, HutoolCommon.HutoolVision.AUTO.getDetail());
|
||||
}
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 传入|的图片列表地址/或者图片Base64编码图片列表(URI形式)
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default void chatVision(String prompt, final List<String> images, final Consumer<String> callback){
|
||||
chatVision(prompt, images, HutoolCommon.HutoolVision.AUTO.getDetail(), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分词:可以将文本转换为模型可理解的 token 信息
|
||||
*
|
||||
* @param text 需要分词的内容
|
||||
* @return 分词结果
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String tokenizeText(String text);
|
||||
|
||||
/**
|
||||
* 文生图
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @return 包含生成图片的url
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String imagesGenerations(String prompt);
|
||||
|
||||
/**
|
||||
* 图文向量化:仅支持单一文本、单张图片或文本与图片的组合输入(即一段文本 + 一张图片),暂不支持批量文本 / 图片的同时处理
|
||||
*
|
||||
* @param text 需要向量化的内容
|
||||
* @param image 需要向量化的图片地址/或者图片Base64编码图片(URI形式)
|
||||
* @return 处理后的向量信息
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String embeddingVision(String text, String image);
|
||||
|
||||
/**
|
||||
* TTS文本转语音
|
||||
*
|
||||
* @param input 需要转成语音的文本
|
||||
* @param voice AI的音色
|
||||
* @return 返回的音频mp3文件流
|
||||
* @since 6.0.0
|
||||
*/
|
||||
InputStream tts(String input, final HutoolCommon.HutoolSpeech voice);
|
||||
|
||||
/**
|
||||
* TTS文本转语音
|
||||
*
|
||||
* @param input 需要转成语音的文本
|
||||
* @return 返回的音频mp3文件流
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default InputStream tts(String input) {
|
||||
return tts(input, HutoolCommon.HutoolSpeech.ALLOY);
|
||||
}
|
||||
|
||||
/**
|
||||
* STT音频转文本
|
||||
*
|
||||
* @param file 需要转成文本的音频文件
|
||||
* @return 返回的文本内容
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String stt(final File file);
|
||||
|
||||
/**
|
||||
* 创建视频生成任务
|
||||
*
|
||||
* @param text 文本提示词
|
||||
* @param image 图片/或者图片Base64编码图片(URI形式)
|
||||
* @param videoParams 视频参数列表
|
||||
* @return 生成任务id
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String videoTasks(String text, String image, final List<HutoolCommon.HutoolVideo> videoParams);
|
||||
|
||||
/**
|
||||
* 创建视频生成任务
|
||||
*
|
||||
* @param text 文本提示词
|
||||
* @param image 图片/或者图片Base64编码图片(URI形式)
|
||||
* @return 生成任务id
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default String videoTasks(String text, String image) {
|
||||
return videoTasks(text, image, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询视频生成任务信息
|
||||
*
|
||||
* @param taskId 通过创建生成视频任务返回的生成任务id
|
||||
* @return 生成任务信息
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String getVideoTasksInfo(String taskId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* 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 org.dromara.hutool.ai.model.hutool;
|
||||
|
||||
import org.dromara.hutool.ai.AIException;
|
||||
import org.dromara.hutool.ai.core.AIConfig;
|
||||
import org.dromara.hutool.ai.core.BaseAIService;
|
||||
import org.dromara.hutool.ai.core.Message;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.thread.ThreadUtil;
|
||||
import org.dromara.hutool.http.client.Response;
|
||||
import org.dromara.hutool.json.JSONUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Hutool服务,AI具体功能的实现
|
||||
*
|
||||
* @author elichow
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class HutoolServiceImpl extends BaseAIService implements HutoolService {
|
||||
|
||||
//对话补全
|
||||
private final String CHAT_ENDPOINT = "/chat/completions";
|
||||
//分词
|
||||
private final String TOKENIZE_TEXT = "/tokenize/text";
|
||||
//文生图
|
||||
private final String IMAGES_GENERATIONS = "/images/generations";
|
||||
//图文向量化
|
||||
private final String EMBEDDING_VISION = "/embeddings/multimodal";
|
||||
//文本转语音
|
||||
private final String TTS = "/audio/tts";
|
||||
//语音转文本
|
||||
private final String STT = "/audio/stt";
|
||||
//创建视频生成任务
|
||||
private final String CREATE_VIDEO = "/video/generations";
|
||||
|
||||
public HutoolServiceImpl(final AIConfig config) {
|
||||
//初始化hutool客户端
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chat(final List<Message> messages) {
|
||||
String paramJson = buildChatRequestBody(messages);
|
||||
Response response = sendPost(CHAT_ENDPOINT, paramJson);
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chat(List<Message> messages,Consumer<String> callback) {
|
||||
Map<String, Object> paramMap = buildChatStreamRequestBody(messages);
|
||||
ThreadUtil.newThread(() -> sendPostStream(CHAT_ENDPOINT, paramMap, callback::accept), "hutool-chat-sse").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chatVision(String prompt, final List<String> images, String detail) {
|
||||
String paramJson = buildChatVisionRequestBody(prompt, images, detail);
|
||||
Response response = sendPost(CHAT_ENDPOINT, paramJson);
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chatVision(String prompt, List<String> images, String detail, Consumer<String> callback) {
|
||||
Map<String, Object> paramMap = buildChatVisionStreamRequestBody(prompt, images, detail);
|
||||
System.out.println(JSONUtil.toJsonStr(paramMap));
|
||||
ThreadUtil.newThread(() -> sendPostStream(CHAT_ENDPOINT, paramMap, callback::accept), "hutool-chatVision-sse").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String tokenizeText(String text) {
|
||||
String paramJson = buildTokenizeRequestBody(text);
|
||||
Response response = sendPost(TOKENIZE_TEXT, paramJson);
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String imagesGenerations(String prompt) {
|
||||
String paramJson = buildImagesGenerationsRequestBody(prompt);
|
||||
Response response = sendPost(IMAGES_GENERATIONS, paramJson);
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String embeddingVision(String text, String image) {
|
||||
String paramJson = buildEmbeddingVisionRequestBody(text, image);
|
||||
Response response = sendPost(EMBEDDING_VISION, paramJson);
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream tts(String input, final HutoolCommon.HutoolSpeech voice) {
|
||||
try {
|
||||
String paramJson = buildTTSRequestBody(input, voice.getVoice());
|
||||
Response response = sendPost(TTS, paramJson);
|
||||
|
||||
// 检查响应内容类型
|
||||
String contentType = response.header("Content-Type");
|
||||
if (contentType != null && contentType.startsWith("application/json")) {
|
||||
// 如果是JSON响应,说明有错误
|
||||
String errorBody = response.bodyStr();
|
||||
throw new AIException("TTS请求失败: " + errorBody);
|
||||
}
|
||||
// 默认返回音频流
|
||||
return response.bodyStream();
|
||||
} catch (Exception e) {
|
||||
throw new AIException("TTS处理失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String stt(final File file) {
|
||||
final Map<String, Object> paramMap = buildSTTRequestBody(file);
|
||||
Response response = sendFormData(STT, paramMap);
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String videoTasks(String text, String image, final List<HutoolCommon.HutoolVideo> videoParams) {
|
||||
String paramJson = buildGenerationsTasksRequestBody(text, image, videoParams);
|
||||
Response response = sendPost(CREATE_VIDEO, paramJson);
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVideoTasksInfo(String taskId) {
|
||||
Response response = sendGet(CREATE_VIDEO + "/" + taskId);
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
|
||||
// 构建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);
|
||||
}
|
||||
|
||||
private Map<String, Object> buildChatStreamRequestBody(final List<Message> messages) {
|
||||
//使用JSON工具
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("stream", true);
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("messages", messages);
|
||||
//合并其他参数
|
||||
paramMap.putAll(config.getAdditionalConfigMap());
|
||||
|
||||
return 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 Map<String, Object> buildChatVisionStreamRequestBody(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("stream", true);
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("messages", messages);
|
||||
//合并其他参数
|
||||
paramMap.putAll(config.getAdditionalConfigMap());
|
||||
return 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);
|
||||
}
|
||||
|
||||
//构建文生图请求体
|
||||
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 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());
|
||||
System.out.println(JSONUtil.toJsonStr(paramMap));
|
||||
return JSONUtil.toJsonStr(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 buildGenerationsTasksRequestBody(String text, String image, final List<HutoolCommon.HutoolVideo> 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.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);
|
||||
content.add(imgUrlMap);
|
||||
}
|
||||
|
||||
//添加视频参数
|
||||
if (videoParams != null && !videoParams.isEmpty()) {
|
||||
//如果有文本参数就加在后面
|
||||
if (textMap != null && !textMap.isEmpty()) {
|
||||
int textIndex = content.indexOf(textMap);
|
||||
StringBuilder textBuilder = new StringBuilder(text);
|
||||
for (HutoolCommon.HutoolVideo 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 (HutoolCommon.HutoolVideo 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());
|
||||
System.out.println(JSONUtil.toJsonStr(paramMap));
|
||||
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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 对hutool的封装实现
|
||||
*
|
||||
* @author elichow
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.ai.model.hutool;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.openai;
|
||||
package org.dromara.hutool.ai.model.openai;
|
||||
|
||||
/**
|
||||
* openai公共类
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.openai;
|
||||
package org.dromara.hutool.ai.model.openai;
|
||||
|
||||
import cn.hutool.v7.ai.Models;
|
||||
import cn.hutool.v7.ai.core.BaseConfig;
|
||||
import org.dromara.hutool.ai.Models;
|
||||
import org.dromara.hutool.ai.core.BaseConfig;
|
||||
|
||||
/**
|
||||
* openai配置类,初始化API接口地址,设置默认的模型
|
||||
@@ -14,10 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.openai;
|
||||
package org.dromara.hutool.ai.model.openai;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIConfig;
|
||||
import cn.hutool.v7.ai.core.AIServiceProvider;
|
||||
import org.dromara.hutool.ai.core.AIConfig;
|
||||
import org.dromara.hutool.ai.core.AIServiceProvider;
|
||||
|
||||
/**
|
||||
* 创建Openai服务实现类
|
||||
@@ -14,14 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.openai;
|
||||
package org.dromara.hutool.ai.model.openai;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIService;
|
||||
import cn.hutool.v7.ai.core.Message;
|
||||
import org.dromara.hutool.ai.core.AIService;
|
||||
import org.dromara.hutool.ai.core.Message;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* openai支持的扩展接口
|
||||
@@ -42,6 +44,17 @@ public interface OpenaiService extends AIService {
|
||||
*/
|
||||
String chatVision(String prompt, final List<String> images, String detail);
|
||||
|
||||
/**
|
||||
* 图像理解-SSE流式输出
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 图片列表/或者图片Base64编码图片列表(URI形式)
|
||||
* @param detail 手动设置图片的质量,取值范围high、low、auto,默认为auto
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
void chatVision(String prompt, final List<String> images, String detail,final Consumer<String> callback);
|
||||
|
||||
/**
|
||||
* 图像理解:模型会依据传入的图片信息以及问题,给出回复。
|
||||
*
|
||||
@@ -54,6 +67,18 @@ public interface OpenaiService extends AIService {
|
||||
return chatVision(prompt, images, OpenaiCommon.OpenaiVision.AUTO.getDetail());
|
||||
}
|
||||
|
||||
/**
|
||||
* 图像理解-SSE流式输出
|
||||
*
|
||||
* @param prompt 题词
|
||||
* @param images 传入的图片列表地址/或者图片Base64编码图片列表(URI形式)
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default void chatVision(String prompt, final List<String> images, final Consumer<String> callback){
|
||||
chatVision(prompt, images, OpenaiCommon.OpenaiVision.AUTO.getDetail(), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文生图 请设置config中model为支持图片功能的模型 DALL·E系列
|
||||
*
|
||||
@@ -166,7 +191,28 @@ public interface OpenaiService extends AIService {
|
||||
* @return AI回答
|
||||
* @since 6.0.0
|
||||
*/
|
||||
String chatReasoning(String prompt, String reasoningEffort);
|
||||
default 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 chatReasoning(messages, reasoningEffort);
|
||||
}
|
||||
|
||||
/**
|
||||
* 推理chat-SSE流式输出
|
||||
* 支持o3-mini和o1
|
||||
*
|
||||
* @param prompt 对话题词
|
||||
* @param reasoningEffort 推理程度
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default void chatReasoning(String prompt, String reasoningEffort, final Consumer<String> callback){
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system", "You are a helpful assistant"));
|
||||
messages.add(new Message("user", prompt));
|
||||
chatReasoning(messages, reasoningEffort, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 推理chat
|
||||
@@ -180,6 +226,18 @@ public interface OpenaiService extends AIService {
|
||||
return chatReasoning(prompt, OpenaiCommon.OpenaiReasoning.MEDIUM.getEffort());
|
||||
}
|
||||
|
||||
/**
|
||||
* 推理chat-SSE流式输出
|
||||
* 支持o3-mini和o1
|
||||
*
|
||||
* @param prompt 对话题词
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default void chatReasoning(String prompt, final Consumer<String> callback) {
|
||||
chatReasoning(prompt, OpenaiCommon.OpenaiReasoning.MEDIUM.getEffort(), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 推理chat
|
||||
* 支持o3-mini和o1
|
||||
@@ -191,6 +249,17 @@ public interface OpenaiService extends AIService {
|
||||
*/
|
||||
String chatReasoning(final List<Message> messages, String reasoningEffort);
|
||||
|
||||
/**
|
||||
* 推理chat-SSE流式输出
|
||||
* 支持o3-mini和o1
|
||||
*
|
||||
* @param messages 消息列表
|
||||
* @param reasoningEffort 推理程度
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
void chatReasoning(final List<Message> messages, String reasoningEffort, final Consumer<String> callback);
|
||||
|
||||
/**
|
||||
* 推理chat
|
||||
* 支持o3-mini和o1
|
||||
@@ -203,4 +272,16 @@ public interface OpenaiService extends AIService {
|
||||
return chatReasoning(messages, OpenaiCommon.OpenaiReasoning.MEDIUM.getEffort());
|
||||
}
|
||||
|
||||
/**
|
||||
* 推理chat-SSE流式输出
|
||||
* 支持o3-mini和o1
|
||||
*
|
||||
* @param messages 消息列表
|
||||
* @param callback 流式数据回调函数
|
||||
* @since 6.0.0
|
||||
*/
|
||||
default void chatReasoning(final List<Message> messages, final Consumer<String> callback) {
|
||||
chatReasoning(messages, OpenaiCommon.OpenaiReasoning.MEDIUM.getEffort(), callback);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,14 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.openai;
|
||||
package org.dromara.hutool.ai.model.openai;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIConfig;
|
||||
import cn.hutool.v7.ai.core.BaseAIService;
|
||||
import cn.hutool.v7.ai.core.Message;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import cn.hutool.v7.http.client.Response;
|
||||
import cn.hutool.v7.json.JSONUtil;
|
||||
import org.dromara.hutool.ai.core.AIConfig;
|
||||
import org.dromara.hutool.ai.core.BaseAIService;
|
||||
import org.dromara.hutool.ai.core.Message;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.thread.ThreadUtil;
|
||||
import org.dromara.hutool.http.client.Response;
|
||||
import org.dromara.hutool.json.JSONUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
@@ -29,6 +30,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* openai服务,AI具体功能的实现
|
||||
@@ -60,15 +62,6 @@ public class OpenaiServiceImpl extends BaseAIService implements OpenaiService {
|
||||
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);
|
||||
@@ -76,6 +69,12 @@ public class OpenaiServiceImpl extends BaseAIService implements OpenaiService {
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chat(List<Message> messages, Consumer<String> callback) {
|
||||
Map<String, Object> paramMap = buildChatStreamRequestBody(messages);
|
||||
ThreadUtil.newThread(() -> sendPostStream(CHAT_ENDPOINT, paramMap, callback::accept), "openai-chat-sse").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String chatVision(String prompt, final List<String> images, String detail) {
|
||||
String paramJson = buildChatVisionRequestBody(prompt, images, detail);
|
||||
@@ -83,6 +82,12 @@ public class OpenaiServiceImpl extends BaseAIService implements OpenaiService {
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chatVision(String prompt, List<String> images, String detail, Consumer<String> callback) {
|
||||
Map<String, Object> paramMap = buildChatVisionStreamRequestBody(prompt, images, detail);
|
||||
ThreadUtil.newThread(() -> sendPostStream(CHAT_ENDPOINT, paramMap, callback::accept), "openai-chatVision-sse").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String imagesGenerations(String prompt) {
|
||||
String paramJson = buildImagesGenerationsRequestBody(prompt);
|
||||
@@ -132,15 +137,6 @@ public class OpenaiServiceImpl extends BaseAIService implements OpenaiService {
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@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);
|
||||
@@ -148,6 +144,12 @@ public class OpenaiServiceImpl extends BaseAIService implements OpenaiService {
|
||||
return response.bodyStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chatReasoning(List<Message> messages, String reasoningEffort, Consumer<String> callback) {
|
||||
Map<String, Object> paramMap = buildChatReasoningStreamRequestBody(messages, reasoningEffort);
|
||||
ThreadUtil.newThread(() -> sendPostStream(CHAT_ENDPOINT, paramMap, callback::accept), "openai-chatReasoning-sse").start();
|
||||
}
|
||||
|
||||
// 构建chat请求体
|
||||
private String buildChatRequestBody(final List<Message> messages) {
|
||||
//使用JSON工具
|
||||
@@ -160,6 +162,18 @@ public class OpenaiServiceImpl extends BaseAIService implements OpenaiService {
|
||||
return JSONUtil.toJsonStr(paramMap);
|
||||
}
|
||||
|
||||
private Map<String, Object> buildChatStreamRequestBody(final List<Message> messages) {
|
||||
//使用JSON工具
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("stream", true);
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("messages", messages);
|
||||
//合并其他参数
|
||||
paramMap.putAll(config.getAdditionalConfigMap());
|
||||
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
//构建chatVision请求体
|
||||
private String buildChatVisionRequestBody(String prompt, final List<String> images, String detail) {
|
||||
// 定义消息结构
|
||||
@@ -191,6 +205,37 @@ public class OpenaiServiceImpl extends BaseAIService implements OpenaiService {
|
||||
return JSONUtil.toJsonStr(paramMap);
|
||||
}
|
||||
|
||||
private Map<String, Object> buildChatVisionStreamRequestBody(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("stream", true);
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("messages", messages);
|
||||
//合并其他参数
|
||||
paramMap.putAll(config.getAdditionalConfigMap());
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
//构建文生图请求体
|
||||
private String buildImagesGenerationsRequestBody(String prompt) {
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
@@ -305,4 +350,16 @@ public class OpenaiServiceImpl extends BaseAIService implements OpenaiService {
|
||||
return JSONUtil.toJsonStr(paramMap);
|
||||
}
|
||||
|
||||
private Map<String, Object> buildChatReasoningStreamRequestBody(final List<Message> messages, String reasoningEffort) {
|
||||
final Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("stream", true);
|
||||
paramMap.put("model", config.getModel());
|
||||
paramMap.put("messages", messages);
|
||||
paramMap.put("reasoning_effort", reasoningEffort);
|
||||
//合并其他参数
|
||||
paramMap.putAll(config.getAdditionalConfigMap());
|
||||
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,4 +21,4 @@
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.openai;
|
||||
package org.dromara.hutool.ai.model.openai;
|
||||
@@ -21,4 +21,4 @@
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model;
|
||||
package org.dromara.hutool.ai.model;
|
||||
@@ -21,4 +21,4 @@
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai;
|
||||
package org.dromara.hutool.ai;
|
||||
@@ -1,4 +0,0 @@
|
||||
cn.hutool.v7.ai.model.deepseek.DeepSeekConfig
|
||||
cn.hutool.v7.ai.model.openai.OpenaiConfig
|
||||
cn.hutool.v7.ai.model.doubao.DoubaoConfig
|
||||
cn.hutool.v7.ai.model.grok.GrokConfig
|
||||
@@ -1,4 +0,0 @@
|
||||
cn.hutool.v7.ai.model.deepseek.DeepSeekProvider
|
||||
cn.hutool.v7.ai.model.openai.OpenaiProvider
|
||||
cn.hutool.v7.ai.model.doubao.DoubaoProvider
|
||||
cn.hutool.v7.ai.model.grok.GrokProvider
|
||||
@@ -0,0 +1,5 @@
|
||||
org.dromara.hutool.ai.model.hutool.HutoolConfig
|
||||
org.dromara.hutool.ai.model.deepseek.DeepSeekConfig
|
||||
org.dromara.hutool.ai.model.openai.OpenaiConfig
|
||||
org.dromara.hutool.ai.model.doubao.DoubaoConfig
|
||||
org.dromara.hutool.ai.model.grok.GrokConfig
|
||||
@@ -0,0 +1,5 @@
|
||||
org.dromara.hutool.ai.model.hutool.HutoolProvider
|
||||
org.dromara.hutool.ai.model.deepseek.DeepSeekProvider
|
||||
org.dromara.hutool.ai.model.openai.OpenaiProvider
|
||||
org.dromara.hutool.ai.model.doubao.DoubaoProvider
|
||||
org.dromara.hutool.ai.model.grok.GrokProvider
|
||||
@@ -14,11 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai;
|
||||
package org.dromara.hutool.ai;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIConfigBuilder;
|
||||
import cn.hutool.v7.ai.core.AIService;
|
||||
import cn.hutool.v7.ai.model.deepseek.DeepSeekService;
|
||||
import org.dromara.hutool.ai.core.AIConfigBuilder;
|
||||
import org.dromara.hutool.ai.core.AIService;
|
||||
import org.dromara.hutool.ai.model.deepseek.DeepSeekService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
@@ -14,15 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai;
|
||||
package org.dromara.hutool.ai;
|
||||
|
||||
import cn.hutool.v7.ai.core.AIConfigBuilder;
|
||||
import cn.hutool.v7.ai.core.AIService;
|
||||
import cn.hutool.v7.ai.core.Message;
|
||||
import cn.hutool.v7.ai.model.deepseek.DeepSeekService;
|
||||
import cn.hutool.v7.ai.model.doubao.DoubaoService;
|
||||
import cn.hutool.v7.ai.model.grok.GrokService;
|
||||
import cn.hutool.v7.ai.model.openai.OpenaiService;
|
||||
import org.dromara.hutool.ai.core.AIConfigBuilder;
|
||||
import org.dromara.hutool.ai.core.AIService;
|
||||
import org.dromara.hutool.ai.core.Message;
|
||||
import org.dromara.hutool.ai.model.deepseek.DeepSeekService;
|
||||
import org.dromara.hutool.ai.model.doubao.DoubaoService;
|
||||
import org.dromara.hutool.ai.model.grok.GrokService;
|
||||
import org.dromara.hutool.ai.model.hutool.HutoolService;
|
||||
import org.dromara.hutool.ai.model.openai.OpenaiService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -46,6 +47,12 @@ class AIUtilTest {
|
||||
assertNotNull(aiService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getHutoolService() {
|
||||
final HutoolService hutoolService = AIUtil.getHutoolService(new AIConfigBuilder(ModelName.HUTOOL.getValue()).setApiKey(key).build());
|
||||
assertNotNull(hutoolService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getDeepSeekService() {
|
||||
final DeepSeekService deepSeekService = AIUtil.getDeepSeekService(new AIConfigBuilder(ModelName.DEEPSEEK.getValue()).setApiKey(key).build());
|
||||
@@ -14,17 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.deepseek;
|
||||
package org.dromara.hutool.ai.model.deepseek;
|
||||
|
||||
import cn.hutool.v7.ai.AIServiceFactory;
|
||||
import cn.hutool.v7.ai.ModelName;
|
||||
import cn.hutool.v7.ai.core.AIConfigBuilder;
|
||||
import cn.hutool.v7.ai.core.Message;
|
||||
import org.dromara.hutool.ai.AIServiceFactory;
|
||||
import org.dromara.hutool.ai.ModelName;
|
||||
import org.dromara.hutool.ai.core.AIConfigBuilder;
|
||||
import org.dromara.hutool.ai.core.Message;
|
||||
import org.dromara.hutool.core.thread.ThreadUtil;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
class DeepSeekServiceTest {
|
||||
|
||||
@@ -35,7 +39,30 @@ class DeepSeekServiceTest {
|
||||
@Disabled
|
||||
void chat(){
|
||||
final String chat = deepSeekService.chat("写一个疯狂星期四广告词");
|
||||
System.out.println(chat);
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chatStream() {
|
||||
String prompt = "写一个疯狂星期四广告词";
|
||||
// 使用AtomicBoolean作为结束标志
|
||||
AtomicBoolean isDone = new AtomicBoolean(false);
|
||||
|
||||
deepSeekService.chat(prompt, data -> {
|
||||
assertNotNull(data);
|
||||
if (data.contains("[DONE]")) {
|
||||
// 设置结束标志
|
||||
isDone.set(true);
|
||||
} else if (data.contains("\"error\"")) {
|
||||
isDone.set(true);
|
||||
}
|
||||
|
||||
});
|
||||
// 轮询检查结束标志
|
||||
while (!isDone.get()) {
|
||||
ThreadUtil.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -45,27 +72,50 @@ class DeepSeekServiceTest {
|
||||
messages.add(new Message("system","你是个抽象大师,会说很抽象的话,最擅长说抽象的笑话"));
|
||||
messages.add(new Message("user","给我说一个笑话"));
|
||||
final String chat = deepSeekService.chat(messages);
|
||||
System.out.println(chat);
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void beta() {
|
||||
final String beta = deepSeekService.beta("写一个疯狂星期四广告词");
|
||||
System.out.println(beta);
|
||||
assertNotNull(beta);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void betaStream() {
|
||||
String beta = "写一个疯狂星期四广告词";
|
||||
// 使用AtomicBoolean作为结束标志
|
||||
AtomicBoolean isDone = new AtomicBoolean(false);
|
||||
|
||||
deepSeekService.beta(beta, data -> {
|
||||
assertNotNull(data);
|
||||
if (data.contains("[DONE]")) {
|
||||
// 设置结束标志
|
||||
isDone.set(true);
|
||||
} else if (data.contains("\"error\"")) {
|
||||
isDone.set(true);
|
||||
}
|
||||
|
||||
});
|
||||
// 轮询检查结束标志
|
||||
while (!isDone.get()) {
|
||||
ThreadUtil.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void models() {
|
||||
final String models = deepSeekService.models();
|
||||
System.out.println(models);
|
||||
assertNotNull(models);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void balance() {
|
||||
final String balance = deepSeekService.balance();
|
||||
System.out.println(balance);
|
||||
assertNotNull(balance);
|
||||
}
|
||||
}
|
||||
@@ -14,14 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.doubao;
|
||||
package org.dromara.hutool.ai.model.doubao;
|
||||
|
||||
import cn.hutool.v7.ai.AIServiceFactory;
|
||||
import cn.hutool.v7.ai.ModelName;
|
||||
import cn.hutool.v7.ai.Models;
|
||||
import cn.hutool.v7.ai.core.AIConfigBuilder;
|
||||
import cn.hutool.v7.ai.core.Message;
|
||||
import cn.hutool.v7.swing.img.ImgUtil;
|
||||
import org.dromara.hutool.ai.AIServiceFactory;
|
||||
import org.dromara.hutool.ai.ModelName;
|
||||
import org.dromara.hutool.ai.Models;
|
||||
import org.dromara.hutool.ai.core.AIConfigBuilder;
|
||||
import org.dromara.hutool.ai.core.Message;
|
||||
import org.dromara.hutool.core.thread.ThreadUtil;
|
||||
import org.dromara.hutool.swing.img.ImgUtil;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -29,6 +30,9 @@ import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
class DoubaoServiceTest {
|
||||
|
||||
@@ -39,7 +43,30 @@ class DoubaoServiceTest {
|
||||
@Disabled
|
||||
void chat(){
|
||||
final String chat = doubaoService.chat("写一个疯狂星期四广告词");
|
||||
System.out.println(chat);
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chatStream() {
|
||||
String prompt = "写一个疯狂星期四广告词";
|
||||
// 使用AtomicBoolean作为结束标志
|
||||
AtomicBoolean isDone = new AtomicBoolean(false);
|
||||
|
||||
doubaoService.chat(prompt, data -> {
|
||||
assertNotNull(data);
|
||||
if (data.contains("[DONE]")) {
|
||||
// 设置结束标志
|
||||
isDone.set(true);
|
||||
} else if (data.contains("\"error\"")) {
|
||||
isDone.set(true);
|
||||
}
|
||||
|
||||
});
|
||||
// 轮询检查结束标志
|
||||
while (!isDone.get()) {
|
||||
ThreadUtil.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -49,7 +76,7 @@ class DoubaoServiceTest {
|
||||
messages.add(new Message("system","你是个抽象大师,会说很抽象的话,最擅长说抽象的笑话"));
|
||||
messages.add(new Message("user","给我说一个笑话"));
|
||||
final String chat = doubaoService.chat(messages);
|
||||
System.out.println(chat);
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -59,7 +86,34 @@ class DoubaoServiceTest {
|
||||
.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));
|
||||
System.out.println(chatVision);
|
||||
assertNotNull(chatVision);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testChatVisionStream() {
|
||||
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);
|
||||
|
||||
String prompt = "图片上有些什么?";
|
||||
List<String> images = Arrays.asList("https://img2.baidu.com/it/u=862000265,4064861820&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1544");
|
||||
|
||||
// 使用AtomicBoolean作为结束标志
|
||||
AtomicBoolean isDone = new AtomicBoolean(false);
|
||||
doubaoService.chatVision(prompt,images, data -> {
|
||||
assertNotNull(data);
|
||||
if (data.contains("[DONE]")) {
|
||||
// 设置结束标志
|
||||
isDone.set(true);
|
||||
} else if (data.contains("\"error\"")) {
|
||||
isDone.set(true);
|
||||
}
|
||||
|
||||
});
|
||||
// 轮询检查结束标志
|
||||
while (!isDone.get()) {
|
||||
ThreadUtil.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -68,17 +122,17 @@ class DoubaoServiceTest {
|
||||
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());
|
||||
System.out.println(chatVision);
|
||||
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);
|
||||
.setApiKey(key).setModel(Models.Doubao.Doubao_Seedance_1_0_lite_i2v.getModel()).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");
|
||||
System.out.println(videoTasks);//cgt-20250306170051-6r9gk
|
||||
assertNotNull(videoTasks);//cgt-20250306170051-6r9gk
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -88,7 +142,7 @@ class DoubaoServiceTest {
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).build(), DoubaoService.class);
|
||||
final String videoTasksInfo = doubaoService.getVideoTasksInfo("cgt-20250306170051-6r9gk");
|
||||
System.out.println(videoTasksInfo);
|
||||
assertNotNull(videoTasksInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -97,7 +151,7 @@ class DoubaoServiceTest {
|
||||
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[]{"阿斯顿", "马丁"});
|
||||
System.out.println(embeddingText);
|
||||
assertNotNull(embeddingText);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -106,7 +160,7 @@ class DoubaoServiceTest {
|
||||
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");
|
||||
System.out.println(embeddingVision);
|
||||
assertNotNull(embeddingVision);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -118,14 +172,41 @@ class DoubaoServiceTest {
|
||||
messages.add(new Message("system","你是什么都可以"));
|
||||
messages.add(new Message("user","你想做些什么"));
|
||||
final String botsChat = doubaoService.botsChat(messages);
|
||||
System.out.println(botsChat);
|
||||
assertNotNull(botsChat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void botsChatStream() {
|
||||
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","你想做些什么"));
|
||||
|
||||
// 使用AtomicBoolean作为结束标志
|
||||
AtomicBoolean isDone = new AtomicBoolean(false);
|
||||
doubaoService.botsChat(messages, data -> {
|
||||
assertNotNull(data);
|
||||
if (data.contains("[DONE]")) {
|
||||
// 设置结束标志
|
||||
isDone.set(true);
|
||||
} else if (data.contains("\"error\"")) {
|
||||
isDone.set(true);
|
||||
}
|
||||
|
||||
});
|
||||
// 轮询检查结束标志
|
||||
while (!isDone.get()) {
|
||||
ThreadUtil.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void tokenization() {
|
||||
final String tokenization = doubaoService.tokenization(new String[]{"阿斯顿", "马丁"});
|
||||
System.out.println(tokenization);
|
||||
assertNotNull(tokenization);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -134,7 +215,7 @@ class DoubaoServiceTest {
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).setModel("your Endpoint ID").build(), DoubaoService.class);
|
||||
final String batchChat = doubaoService.batchChat("写首歌词");
|
||||
System.out.println(batchChat);
|
||||
assertNotNull(batchChat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -146,7 +227,7 @@ class DoubaoServiceTest {
|
||||
messages.add(new Message("system","你是个抽象大师"));
|
||||
messages.add(new Message("user","写一个KFC的抽象广告"));
|
||||
final String batchChat = doubaoService.batchChat(messages);
|
||||
System.out.println(batchChat);
|
||||
assertNotNull(batchChat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -157,7 +238,7 @@ class DoubaoServiceTest {
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system","你是个抽象大师,你真的很抽象"));
|
||||
final String context = doubaoService.createContext(messages);//ctx-20250307092153-cvslm
|
||||
System.out.println(context);
|
||||
assertNotNull(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -168,17 +249,16 @@ class DoubaoServiceTest {
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system","你是个抽象大师,你真的很抽象"));
|
||||
final String context = doubaoService.createContext(messages,DoubaoCommon.DoubaoContext.COMMON_PREFIX.getMode());
|
||||
System.out.println(context);//ctx-20250307092153-cvslm
|
||||
assertNotNull(context);
|
||||
}
|
||||
|
||||
@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");
|
||||
System.out.println(chatContext);
|
||||
final String chatContext = doubaoService.chatContext("你是谁?", "your contextId");
|
||||
assertNotNull(chatContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -188,7 +268,43 @@ class DoubaoServiceTest {
|
||||
.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");
|
||||
System.out.println(chatContext);
|
||||
final String chatContext = doubaoService.chatContext(messages, "your contextId");
|
||||
assertNotNull(chatContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testChatContextStream() {
|
||||
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","你怎么看待意大利面拌水泥?"));
|
||||
String contextId = "your contextId";
|
||||
|
||||
// 使用AtomicBoolean作为结束标志
|
||||
AtomicBoolean isDone = new AtomicBoolean(false);
|
||||
doubaoService.chatContext(messages,contextId, data -> {
|
||||
assertNotNull(data);
|
||||
if (data.contains("[DONE]")) {
|
||||
// 设置结束标志
|
||||
isDone.set(true);
|
||||
} else if (data.contains("\"error\"")) {
|
||||
isDone.set(true);
|
||||
}
|
||||
|
||||
});
|
||||
// 轮询检查结束标志
|
||||
while (!isDone.get()) {
|
||||
ThreadUtil.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void imagesGenerations() {
|
||||
final DoubaoService doubaoService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.DOUBAO.getValue())
|
||||
.setApiKey(key).setModel(Models.Doubao.DOUBAO_SEEDREAM_3_0_T2I.getModel()).build(), DoubaoService.class);
|
||||
final String imagesGenerations = doubaoService.imagesGenerations("一位年轻的宇航员站在未来感十足的太空站内,透过巨大的弧形落地窗凝望浩瀚宇宙。窗外,璀璨的星河与五彩斑斓的星云交织,远处隐约可见未知星球的轮廓,仿佛在召唤着探索的脚步。宇航服上的呼吸灯与透明显示屏上的星图交相辉映,象征着人类科技与宇宙奥秘的碰撞。画面深邃而神秘,充满对未知的渴望与无限可能的想象。");
|
||||
assertNotNull(imagesGenerations);
|
||||
}
|
||||
}
|
||||
@@ -14,14 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.grok;
|
||||
package org.dromara.hutool.ai.model.grok;
|
||||
|
||||
import cn.hutool.v7.ai.AIServiceFactory;
|
||||
import cn.hutool.v7.ai.ModelName;
|
||||
import cn.hutool.v7.ai.Models;
|
||||
import cn.hutool.v7.ai.core.AIConfigBuilder;
|
||||
import cn.hutool.v7.ai.core.Message;
|
||||
import cn.hutool.v7.swing.img.ImgUtil;
|
||||
import org.dromara.hutool.ai.AIServiceFactory;
|
||||
import org.dromara.hutool.ai.ModelName;
|
||||
import org.dromara.hutool.ai.Models;
|
||||
import org.dromara.hutool.ai.core.AIConfigBuilder;
|
||||
import org.dromara.hutool.ai.core.Message;
|
||||
import org.dromara.hutool.core.thread.ThreadUtil;
|
||||
import org.dromara.hutool.swing.img.ImgUtil;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -29,6 +30,7 @@ import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@@ -42,7 +44,30 @@ class GrokServiceTest {
|
||||
@Disabled
|
||||
void chat(){
|
||||
final String chat = grokService.chat("写一个疯狂星期四广告词");
|
||||
System.out.println(chat);
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chatStream() {
|
||||
String prompt = "写一个疯狂星期四广告词";
|
||||
// 使用AtomicBoolean作为结束标志
|
||||
AtomicBoolean isDone = new AtomicBoolean(false);
|
||||
|
||||
grokService.chat(prompt, data -> {
|
||||
assertNotNull(data);
|
||||
if (data.contains("[DONE]")) {
|
||||
// 设置结束标志
|
||||
isDone.set(true);
|
||||
} else if (data.contains("\"error\"")) {
|
||||
isDone.set(true);
|
||||
}
|
||||
|
||||
});
|
||||
// 轮询检查结束标志
|
||||
while (!isDone.get()) {
|
||||
ThreadUtil.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -52,14 +77,37 @@ class GrokServiceTest {
|
||||
messages.add(new Message("system","你是个抽象大师,会说很抽象的话,最擅长说抽象的笑话"));
|
||||
messages.add(new Message("user","给我说一个笑话"));
|
||||
final String chat = grokService.chat(messages);
|
||||
System.out.println(chat);
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void message() {
|
||||
final String message = grokService.message("给我一个KFC的广告词", 4096);
|
||||
System.out.println(message);
|
||||
assertNotNull(message);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void messageStream() {
|
||||
String prompt = "给我一个KFC的广告词";
|
||||
|
||||
// 使用AtomicBoolean作为结束标志
|
||||
AtomicBoolean isDone = new AtomicBoolean(false);
|
||||
grokService.message(prompt, 4096, data -> {
|
||||
assertNotNull(data);
|
||||
if (data.contains("[DONE]")) {
|
||||
// 设置结束标志
|
||||
isDone.set(true);
|
||||
} else if (data.contains("\"error\"")) {
|
||||
isDone.set(true);
|
||||
}
|
||||
|
||||
});
|
||||
// 轮询检查结束标志
|
||||
while (!isDone.get()) {
|
||||
ThreadUtil.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -68,7 +116,32 @@ class GrokServiceTest {
|
||||
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));
|
||||
System.out.println(chatVision);
|
||||
assertNotNull(chatVision);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testChatVisionStream() {
|
||||
final GrokService grokService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.GROK.getValue()).setModel(Models.Grok.GROK_2_VISION_1212.getModel()).setApiKey(key).build(), GrokService.class);
|
||||
String prompt = "图片上有些什么?";
|
||||
List<String> images = Arrays.asList("https://img2.baidu.com/it/u=862000265,4064861820&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1544");
|
||||
|
||||
// 使用AtomicBoolean作为结束标志
|
||||
AtomicBoolean isDone = new AtomicBoolean(false);
|
||||
grokService.chatVision(prompt,images, data -> {
|
||||
assertNotNull(data);
|
||||
if (data.contains("[DONE]")) {
|
||||
// 设置结束标志
|
||||
isDone.set(true);
|
||||
} else if (data.contains("\"error\"")) {
|
||||
isDone.set(true);
|
||||
}
|
||||
|
||||
});
|
||||
// 轮询检查结束标志
|
||||
while (!isDone.get()) {
|
||||
ThreadUtil.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -76,7 +149,7 @@ class GrokServiceTest {
|
||||
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"));
|
||||
System.out.println(chatVision);
|
||||
assertNotNull(chatVision);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -120,4 +193,13 @@ class GrokServiceTest {
|
||||
final String deferred = grokService.deferredCompletion(key);
|
||||
assertNotNull(deferred);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void imagesGenerations() {
|
||||
final GrokService grokService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.GROK.getValue())
|
||||
.setApiKey(key).setModel(Models.Grok.GROK_2_IMAGE.getModel()).build(), GrokService.class);
|
||||
final String imagesGenerations = grokService.imagesGenerations("一位年轻的宇航员站在未来感十足的太空站内,透过巨大的弧形落地窗凝望浩瀚宇宙。窗外,璀璨的星河与五彩斑斓的星云交织,远处隐约可见未知星球的轮廓,仿佛在召唤着探索的脚步。宇航服上的呼吸灯与透明显示屏上的星图交相辉映,象征着人类科技与宇宙奥秘的碰撞。画面深邃而神秘,充满对未知的渴望与无限可能的想象。");
|
||||
assertNotNull(imagesGenerations);
|
||||
}
|
||||
}
|
||||
@@ -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 org.dromara.hutool.ai.model.hutool;
|
||||
|
||||
import org.dromara.hutool.ai.AIException;
|
||||
import org.dromara.hutool.ai.AIServiceFactory;
|
||||
import org.dromara.hutool.ai.ModelName;
|
||||
import org.dromara.hutool.ai.core.AIConfigBuilder;
|
||||
import org.dromara.hutool.ai.core.Message;
|
||||
import org.dromara.hutool.core.io.file.FileUtil;
|
||||
import org.dromara.hutool.core.thread.ThreadUtil;
|
||||
import org.dromara.hutool.swing.img.ImgUtil;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class HutoolServiceTest {
|
||||
|
||||
String key = "请前往Hutool-AI官网:https://ai.hutool.cn 获取";
|
||||
HutoolService hutoolService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.HUTOOL.getValue()).setApiKey(key).build(), HutoolService.class);
|
||||
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chat(){
|
||||
final String chat = hutoolService.chat("写一个疯狂星期四广告词");
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chatStream() {
|
||||
String prompt = "写一个疯狂星期四广告词";
|
||||
// 使用AtomicBoolean作为结束标志
|
||||
AtomicBoolean isDone = new AtomicBoolean(false);
|
||||
|
||||
hutoolService.chat(prompt, data -> {
|
||||
assertNotNull(data);
|
||||
if (data.contains("[DONE]")) {
|
||||
// 设置结束标志
|
||||
isDone.set(true);
|
||||
} else if (data.contains("\"error\"")) {
|
||||
isDone.set(true);
|
||||
}
|
||||
|
||||
});
|
||||
// 轮询检查结束标志
|
||||
while (!isDone.get()) {
|
||||
ThreadUtil.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testChat(){
|
||||
final List<Message> messages = new ArrayList<>();
|
||||
messages.add(new Message("system","你是个抽象大师,会说很抽象的话,最擅长说抽象的笑话"));
|
||||
messages.add(new Message("user","给我说一个笑话"));
|
||||
final String chat = hutoolService.chat(messages);
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chatVision() {
|
||||
final String base64 = ImgUtil.toBase64DataUri(Toolkit.getDefaultToolkit().createImage("your imageUrl"), "png");
|
||||
final String chatVision = hutoolService.chatVision("图片上有些什么?", Arrays.asList(base64));
|
||||
assertNotNull(chatVision);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testChatVisionStream() {
|
||||
String prompt = "图片上有些什么?";
|
||||
List<String> images = Arrays.asList("https://img2.baidu.com/it/u=862000265,4064861820&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1544");
|
||||
|
||||
// 使用AtomicBoolean作为结束标志
|
||||
AtomicBoolean isDone = new AtomicBoolean(false);
|
||||
hutoolService.chatVision(prompt,images, data -> {
|
||||
assertNotNull(data);
|
||||
if (data.contains("[DONE]")) {
|
||||
// 设置结束标志
|
||||
isDone.set(true);
|
||||
} else if (data.contains("\"error\"")) {
|
||||
isDone.set(true);
|
||||
}
|
||||
|
||||
});
|
||||
// 轮询检查结束标志
|
||||
while (!isDone.get()) {
|
||||
ThreadUtil.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testChatVision() {
|
||||
final String chatVision = hutoolService.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 tokenizeText() {
|
||||
final String tokenizeText = hutoolService.tokenizeText(key);
|
||||
assertNotNull(tokenizeText);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void imagesGenerations() {
|
||||
final String imagesGenerations = hutoolService.imagesGenerations("一位年轻的宇航员站在未来感十足的太空站内,透过巨大的弧形落地窗凝望浩瀚宇宙。窗外,璀璨的星河与五彩斑斓的星云交织,远处隐约可见未知星球的轮廓,仿佛在召唤着探索的脚步。宇航服上的呼吸灯与透明显示屏上的星图交相辉映,象征着人类科技与宇宙奥秘的碰撞。画面深邃而神秘,充满对未知的渴望与无限可能的想象。");
|
||||
assertNotNull(imagesGenerations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void embeddingVision() {
|
||||
final String embeddingVision = hutoolService.embeddingVision("天空好难", "https://img2.baidu.com/it/u=862000265,4064861820&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1544");
|
||||
System.out.println(embeddingVision);
|
||||
assertNotNull(embeddingVision);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void textToSpeech() {
|
||||
try {
|
||||
// 测试正常音频流返回
|
||||
final InputStream inputStream = hutoolService.tts("万里山河一夜白,\n" +
|
||||
"千峰尽染玉龙哀。\n" +
|
||||
"长风卷起琼花碎,\n" +
|
||||
"直上九霄揽月来。", HutoolCommon.HutoolSpeech.NOVA);
|
||||
assertNotNull(inputStream);
|
||||
|
||||
// 保存音频文件
|
||||
final String filePath = "your filePath";
|
||||
FileUtil.writeFromStream(inputStream, new File(filePath));
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new AIException("TTS测试失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void speechToText() {
|
||||
final File file = FileUtil.file("your filePath");
|
||||
final String speechToText = hutoolService.stt(file);
|
||||
assertNotNull(speechToText);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void videoTasks() {
|
||||
final String videoTasks = hutoolService.videoTasks("生成一段动画视频,主角是大耳朵图图,一个活泼可爱的小男孩。视频中图图在公园里玩耍," +
|
||||
"画面采用明亮温暖的卡通风格,色彩鲜艳,动作流畅。背景音乐轻快活泼,带有冒险感,音效包括鸟叫声、欢笑声和山洞回声。", "https://img2.baidu.com/it/u=862000265,4064861820&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1544");
|
||||
assertNotNull(videoTasks);//cgt-20250529154621-d7dq9
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void getVideoTasksInfo() {
|
||||
final String videoTasksInfo = hutoolService.getVideoTasksInfo("cgt-20250529154621-d7dq9");
|
||||
System.out.println(videoTasksInfo);
|
||||
assertNotNull(videoTasksInfo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,14 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.ai.model.openai;
|
||||
package org.dromara.hutool.ai.model.openai;
|
||||
|
||||
import cn.hutool.v7.ai.AIServiceFactory;
|
||||
import cn.hutool.v7.ai.ModelName;
|
||||
import cn.hutool.v7.ai.Models;
|
||||
import cn.hutool.v7.ai.core.AIConfigBuilder;
|
||||
import cn.hutool.v7.ai.core.Message;
|
||||
import cn.hutool.v7.core.io.file.FileUtil;
|
||||
import org.dromara.hutool.ai.AIServiceFactory;
|
||||
import org.dromara.hutool.ai.ModelName;
|
||||
import org.dromara.hutool.ai.Models;
|
||||
import org.dromara.hutool.ai.core.AIConfigBuilder;
|
||||
import org.dromara.hutool.ai.core.Message;
|
||||
import org.dromara.hutool.core.io.file.FileUtil;
|
||||
import org.dromara.hutool.core.thread.ThreadUtil;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -35,6 +36,9 @@ import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
class OpenaiServiceTest {
|
||||
|
||||
@@ -46,7 +50,30 @@ class OpenaiServiceTest {
|
||||
@Disabled
|
||||
void chat(){
|
||||
final String chat = openaiService.chat("写一个疯狂星期四广告词");
|
||||
System.out.println(chat);
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chatStream() {
|
||||
String prompt = "写一个疯狂星期四广告词";
|
||||
// 使用AtomicBoolean作为结束标志
|
||||
AtomicBoolean isDone = new AtomicBoolean(false);
|
||||
|
||||
openaiService.chat(prompt, data -> {
|
||||
assertNotNull(data);
|
||||
if (data.contains("[DONE]")) {
|
||||
// 设置结束标志
|
||||
isDone.set(true);
|
||||
} else if (data.contains("\"error\"")) {
|
||||
isDone.set(true);
|
||||
}
|
||||
|
||||
});
|
||||
// 轮询检查结束标志
|
||||
while (!isDone.get()) {
|
||||
ThreadUtil.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -56,7 +83,33 @@ class OpenaiServiceTest {
|
||||
messages.add(new Message("system","你是个抽象大师,会说很抽象的话,最擅长说抽象的笑话"));
|
||||
messages.add(new Message("user","给我说一个笑话"));
|
||||
final String chat = openaiService.chat(messages);
|
||||
System.out.println(chat);
|
||||
assertNotNull(chat);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void testChatVisionStream() {
|
||||
final OpenaiService openaiService = AIServiceFactory.getAIService(new AIConfigBuilder(ModelName.OPENAI.getValue())
|
||||
.setApiKey(key).setModel(Models.Openai.GPT_4O_MINI.getModel()).build(), OpenaiService.class);
|
||||
String prompt = "图片上有些什么?";
|
||||
List<String> images = 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");
|
||||
|
||||
// 使用AtomicBoolean作为结束标志
|
||||
AtomicBoolean isDone = new AtomicBoolean(false);
|
||||
openaiService.chatVision(prompt,images, data -> {
|
||||
assertNotNull(data);
|
||||
if (data.contains("[DONE]")) {
|
||||
// 设置结束标志
|
||||
isDone.set(true);
|
||||
} else if (data.contains("\"error\"")) {
|
||||
isDone.set(true);
|
||||
}
|
||||
|
||||
});
|
||||
// 轮询检查结束标志
|
||||
while (!isDone.get()) {
|
||||
ThreadUtil.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -65,7 +118,7 @@ class OpenaiServiceTest {
|
||||
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"));
|
||||
System.out.println(chatVision);
|
||||
assertNotNull(chatVision);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -74,8 +127,7 @@ class OpenaiServiceTest {
|
||||
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("一位年轻的宇航员站在未来感十足的太空站内,透过巨大的弧形落地窗凝望浩瀚宇宙。窗外,璀璨的星河与五彩斑斓的星云交织,远处隐约可见未知星球的轮廓,仿佛在召唤着探索的脚步。宇航服上的呼吸灯与透明显示屏上的星图交相辉映,象征着人类科技与宇宙奥秘的碰撞。画面深邃而神秘,充满对未知的渴望与无限可能的想象。");
|
||||
System.out.println(imagesGenerations);
|
||||
//https://oaidalleapiprodscus.blob.core.windows.net/private/org-l99H6T0zCZejctB2TqdYrXFB/user-LilDVU1V8cUxJYwVAGRkUwYd/img-yA9kNatHnBiUHU5lZGim1hP2.png?st=2025-03-07T01%3A04%3A18Z&se=2025-03-07T03%3A04%3A18Z&sp=r&sv=2024-08-04&sr=b&rscd=inline&rsct=image/png&skoid=d505667d-d6c1-4a0a-bac7-5c84a87759f8&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2025-03-06T15%3A04%3A42Z&ske=2025-03-07T15%3A04%3A42Z&sks=b&skv=2024-08-04&sig=rjcRzC5U7Y3pEDZ4ME0CiviAPdIpoGO2rRTXw3m8rHw%3D
|
||||
assertNotNull(imagesGenerations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -85,7 +137,7 @@ class OpenaiServiceTest {
|
||||
.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);
|
||||
System.out.println(imagesEdits);
|
||||
assertNotNull(imagesEdits);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -95,7 +147,7 @@ class OpenaiServiceTest {
|
||||
.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);
|
||||
System.out.println(imagesVariations);
|
||||
assertNotNull(imagesVariations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -130,7 +182,7 @@ class OpenaiServiceTest {
|
||||
.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
|
||||
@@ -139,7 +191,7 @@ class OpenaiServiceTest {
|
||||
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("萬里山河一夜白,千峰盡染玉龍哀,長風捲起瓊花碎,直上九霄闌月來");
|
||||
System.out.println(embeddingText);
|
||||
assertNotNull(embeddingText);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -148,7 +200,7 @@ class OpenaiServiceTest {
|
||||
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");
|
||||
System.out.println(moderations);
|
||||
assertNotNull(moderations);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -160,6 +212,33 @@ class OpenaiServiceTest {
|
||||
messages.add(new Message("system","你是现代抽象家"));
|
||||
messages.add(new Message("user","给我一个KFC疯狂星期四的文案"));
|
||||
final String chatReasoning = openaiService.chatReasoning(messages, OpenaiCommon.OpenaiReasoning.HIGH.getEffort());
|
||||
System.out.println(chatReasoning);
|
||||
assertNotNull(chatReasoning);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
void chatReasoningStream() {
|
||||
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疯狂星期四的文案"));
|
||||
|
||||
// 使用AtomicBoolean作为结束标志
|
||||
AtomicBoolean isDone = new AtomicBoolean(false);
|
||||
openaiService.chatReasoning(messages,OpenaiCommon.OpenaiReasoning.HIGH.getEffort(), data -> {
|
||||
assertNotNull(data);
|
||||
if (data.contains("[DONE]")) {
|
||||
// 设置结束标志
|
||||
isDone.set(true);
|
||||
} else if (data.contains("\"error\"")) {
|
||||
isDone.set(true);
|
||||
}
|
||||
|
||||
});
|
||||
// 轮询检查结束标志
|
||||
while (!isDone.get()) {
|
||||
ThreadUtil.sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,9 @@
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.hutool.v7</groupId>
|
||||
<groupId>org.dromara.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>7.0.0-M1</version>
|
||||
<version>6.0.0-M23</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-all</artifactId>
|
||||
@@ -33,7 +33,7 @@
|
||||
<description>
|
||||
Hutool是一个功能丰富且易用的Java工具库,通过诸多实用工具类的使用,旨在帮助开发者快速、便捷地完成各类开发任务。这些封装的工具涵盖了字符串、数字、集合、编码、日期、文件、IO、加密、数据库JDBC、JSON、HTTP客户端等一系列操作,可以满足各种不同的开发需求。
|
||||
</description>
|
||||
<url>https://github.com/chinabugotech/hutool</url>
|
||||
<url>https://github.com/dromara/hutool</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7;
|
||||
package org.dromara.hutool;
|
||||
|
||||
import cn.hutool.v7.core.lang.ConsoleTable;
|
||||
import cn.hutool.v7.core.reflect.ClassUtil;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.lang.ConsoleTable;
|
||||
import org.dromara.hutool.core.reflect.ClassUtil;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@@ -60,7 +60,7 @@ public class Hutool {
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public static Set<Class<?>> getAllUtils() {
|
||||
return ClassUtil.scanPackage("cn.hutool.v7",
|
||||
return ClassUtil.scanPackage("org.dromara.hutool",
|
||||
(clazz) -> (!clazz.isInterface()) && StrUtil.endWith(clazz.getSimpleName(), "Util"));
|
||||
}
|
||||
|
||||
@@ -23,4 +23,4 @@
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
package cn.hutool.v7;
|
||||
package org.dromara.hutool;
|
||||
@@ -23,15 +23,15 @@
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.hutool.v7</groupId>
|
||||
<groupId>org.dromara.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>7.0.0-M1</version>
|
||||
<version>6.0.0-M23</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-bom</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>提供丰富的Java工具方法,此模块为Hutool所有模块汇总,最终形式为拆分开的多个jar包,可以通过exclude方式排除不需要的模块</description>
|
||||
<url>https://github.com/chinabugotech/hutool</url>
|
||||
<url>https://github.com/dromara/hutool</url>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.hutool.v7</groupId>
|
||||
<groupId>org.dromara.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>7.0.0-M1</version>
|
||||
<version>6.0.0-M23</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-core</artifactId>
|
||||
@@ -33,7 +33,7 @@
|
||||
<description>Hutool核心,包括集合、字符串、Bean等工具</description>
|
||||
|
||||
<properties>
|
||||
<Automatic-Module-Name>cn.hutool.v7.core</Automatic-Module-Name>
|
||||
<Automatic-Module-Name>org.dromara.hutool.core</Automatic-Module-Name>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-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.v7.core.bean;
|
||||
|
||||
import cn.hutool.v7.core.bean.copier.ValueProvider;
|
||||
import cn.hutool.v7.core.reflect.ConstructorUtil;
|
||||
|
||||
import java.lang.reflect.RecordComponent;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* java.lang.Record 相关工具类封装
|
||||
*
|
||||
* @author Looly
|
||||
* @since 7.0.0
|
||||
*/
|
||||
public class RecordUtil {
|
||||
|
||||
/**
|
||||
* 判断给定类是否为Record类
|
||||
*
|
||||
* @param clazz 类
|
||||
* @return 是否为Record类
|
||||
*/
|
||||
public static boolean isRecord(final Class<?> clazz) {
|
||||
return null != clazz && clazz.isRecord();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Record类中所有字段名称,getter方法名与字段同名
|
||||
*
|
||||
* @param recordClass Record类
|
||||
* @return 字段数组
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Map.Entry<String, Type>[] getRecordComponents(final Class<?> recordClass) {
|
||||
final RecordComponent[] components = recordClass.getRecordComponents();
|
||||
final Map.Entry<String, Type>[] entries = new Map.Entry[components.length];
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
entries[i] = new AbstractMap.SimpleEntry<>(components[i].getName(), components[i].getGenericType());
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化Record类
|
||||
*
|
||||
* @param recordClass 类
|
||||
* @param valueProvider 参数值提供器
|
||||
* @return Record类
|
||||
*/
|
||||
public static Object newInstance(final Class<?> recordClass, final ValueProvider<String> valueProvider) {
|
||||
final Map.Entry<String, Type>[] recordComponents = getRecordComponents(recordClass);
|
||||
final Object[] args = new Object[recordComponents.length];
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
args[i] = valueProvider.value(recordComponents[i].getKey(), recordComponents[i].getValue());
|
||||
}
|
||||
|
||||
return ConstructorUtil.newInstance(recordClass, args);
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-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.v7.core.thread.lock;
|
||||
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
|
||||
/**
|
||||
* 锁相关工具
|
||||
*
|
||||
* @author Looly
|
||||
* @since 5.2.5
|
||||
*/
|
||||
public class LockUtil {
|
||||
|
||||
private static final NoLock NO_LOCK = new NoLock();
|
||||
|
||||
/**
|
||||
* 创建{@link StampedLock}锁
|
||||
*
|
||||
* @return {@link StampedLock}锁
|
||||
*/
|
||||
public static StampedLock createStampLock() {
|
||||
return new StampedLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建{@link ReentrantReadWriteLock}锁
|
||||
*
|
||||
* @param fair 是否公平锁
|
||||
* @return {@link ReentrantReadWriteLock}锁
|
||||
*/
|
||||
public static ReentrantReadWriteLock createReadWriteLock(final boolean fair) {
|
||||
return new ReentrantReadWriteLock(fair);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例的无锁对象
|
||||
*
|
||||
* @return {@link NoLock}
|
||||
*/
|
||||
public static NoLock getNoLock(){
|
||||
return NO_LOCK;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 锁的实现
|
||||
*
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.v7.core.thread.lock;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation;
|
||||
package org.dromara.hutool.core.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation;
|
||||
package org.dromara.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.v7.core.annotation.elements.HierarchicalAnnotatedElements;
|
||||
import cn.hutool.v7.core.annotation.elements.MetaAnnotatedElement;
|
||||
import cn.hutool.v7.core.annotation.elements.RepeatableMetaAnnotatedElement;
|
||||
import cn.hutool.v7.core.array.ArrayUtil;
|
||||
import cn.hutool.v7.core.map.reference.WeakConcurrentMap;
|
||||
import cn.hutool.v7.core.util.ObjUtil;
|
||||
import org.dromara.hutool.core.annotation.elements.HierarchicalAnnotatedElements;
|
||||
import org.dromara.hutool.core.annotation.elements.MetaAnnotatedElement;
|
||||
import org.dromara.hutool.core.annotation.elements.RepeatableMetaAnnotatedElement;
|
||||
import org.dromara.hutool.core.array.ArrayUtil;
|
||||
import org.dromara.hutool.core.map.reference.WeakConcurrentMap;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Inherited;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation;
|
||||
package org.dromara.hutool.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -14,10 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation;
|
||||
package org.dromara.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.v7.core.reflect.method.MethodUtil;
|
||||
import cn.hutool.v7.core.text.CharSequenceUtil;
|
||||
import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap;
|
||||
import org.dromara.hutool.core.reflect.method.MethodUtil;
|
||||
import org.dromara.hutool.core.text.CharSequenceUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
@@ -27,7 +28,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -96,7 +96,7 @@ public final class AnnotationMappingProxy<T extends Annotation> implements Invoc
|
||||
private AnnotationMappingProxy(final AnnotationMapping<T> annotation) {
|
||||
final int methodCount = annotation.getAttributes().length;
|
||||
this.methods = new HashMap<>(methodCount + 5);
|
||||
this.valueCache = new ConcurrentHashMap<>(methodCount);
|
||||
this.valueCache = new SafeConcurrentHashMap<>(methodCount);
|
||||
this.mapping = annotation;
|
||||
loadMethods();
|
||||
}
|
||||
@@ -14,12 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation;
|
||||
package org.dromara.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.v7.core.reflect.method.MethodUtil;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.reflect.method.MethodUtil;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
@@ -14,21 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation;
|
||||
package org.dromara.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.v7.core.annotation.elements.CombinationAnnotatedElement;
|
||||
import cn.hutool.v7.core.array.ArrayUtil;
|
||||
import cn.hutool.v7.core.classloader.ClassLoaderUtil;
|
||||
import cn.hutool.v7.core.exception.HutoolException;
|
||||
import cn.hutool.v7.core.func.LambdaInfo;
|
||||
import cn.hutool.v7.core.func.LambdaUtil;
|
||||
import cn.hutool.v7.core.func.SerFunction;
|
||||
import cn.hutool.v7.core.map.reference.WeakConcurrentMap;
|
||||
import cn.hutool.v7.core.reflect.FieldUtil;
|
||||
import cn.hutool.v7.core.reflect.method.MethodUtil;
|
||||
import cn.hutool.v7.core.text.CharSequenceUtil;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import cn.hutool.v7.core.util.ObjUtil;
|
||||
import org.dromara.hutool.core.annotation.elements.CombinationAnnotatedElement;
|
||||
import org.dromara.hutool.core.array.ArrayUtil;
|
||||
import org.dromara.hutool.core.classloader.ClassLoaderUtil;
|
||||
import org.dromara.hutool.core.exception.HutoolException;
|
||||
import org.dromara.hutool.core.func.LambdaInfo;
|
||||
import org.dromara.hutool.core.func.LambdaUtil;
|
||||
import org.dromara.hutool.core.func.SerFunction;
|
||||
import org.dromara.hutool.core.map.reference.WeakConcurrentMap;
|
||||
import org.dromara.hutool.core.reflect.FieldUtil;
|
||||
import org.dromara.hutool.core.reflect.method.MethodUtil;
|
||||
import org.dromara.hutool.core.text.CharSequenceUtil;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.*;
|
||||
@@ -14,11 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation;
|
||||
package org.dromara.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.v7.core.reflect.ClassUtil;
|
||||
import cn.hutool.v7.core.reflect.method.MethodUtil;
|
||||
import cn.hutool.v7.core.text.CharSequenceUtil;
|
||||
import org.dromara.hutool.core.reflect.ClassUtil;
|
||||
import org.dromara.hutool.core.reflect.method.MethodUtil;
|
||||
import org.dromara.hutool.core.text.CharSequenceUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation;
|
||||
package org.dromara.hutool.core.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation;
|
||||
package org.dromara.hutool.core.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation;
|
||||
package org.dromara.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.v7.core.array.ArrayUtil;
|
||||
import cn.hutool.v7.core.collection.CollUtil;
|
||||
import cn.hutool.v7.core.map.reference.WeakConcurrentMap;
|
||||
import cn.hutool.v7.core.reflect.method.MethodUtil;
|
||||
import cn.hutool.v7.core.text.CharSequenceUtil;
|
||||
import org.dromara.hutool.core.array.ArrayUtil;
|
||||
import org.dromara.hutool.core.collection.CollUtil;
|
||||
import org.dromara.hutool.core.map.reference.WeakConcurrentMap;
|
||||
import org.dromara.hutool.core.reflect.method.MethodUtil;
|
||||
import org.dromara.hutool.core.text.CharSequenceUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Repeatable;
|
||||
@@ -14,16 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation;
|
||||
package org.dromara.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.v7.core.annotation.elements.MetaAnnotatedElement;
|
||||
import cn.hutool.v7.core.collection.CollUtil;
|
||||
import cn.hutool.v7.core.lang.Assert;
|
||||
import cn.hutool.v7.core.map.multi.Graph;
|
||||
import cn.hutool.v7.core.reflect.ClassUtil;
|
||||
import cn.hutool.v7.core.reflect.method.MethodUtil;
|
||||
import cn.hutool.v7.core.text.CharSequenceUtil;
|
||||
import cn.hutool.v7.core.array.ArrayUtil;
|
||||
import org.dromara.hutool.core.annotation.elements.MetaAnnotatedElement;
|
||||
import org.dromara.hutool.core.collection.CollUtil;
|
||||
import org.dromara.hutool.core.lang.Assert;
|
||||
import org.dromara.hutool.core.map.multi.Graph;
|
||||
import org.dromara.hutool.core.reflect.ClassUtil;
|
||||
import org.dromara.hutool.core.reflect.method.MethodUtil;
|
||||
import org.dromara.hutool.core.text.CharSequenceUtil;
|
||||
import org.dromara.hutool.core.array.ArrayUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation;
|
||||
package org.dromara.hutool.core.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation.elements;
|
||||
package org.dromara.hutool.core.annotation.elements;
|
||||
|
||||
import cn.hutool.v7.core.annotation.AnnotationUtil;
|
||||
import cn.hutool.v7.core.array.ArrayUtil;
|
||||
import cn.hutool.v7.core.collection.set.SetUtil;
|
||||
import cn.hutool.v7.core.map.TableMap;
|
||||
import org.dromara.hutool.core.annotation.AnnotationUtil;
|
||||
import org.dromara.hutool.core.array.ArrayUtil;
|
||||
import org.dromara.hutool.core.collection.set.SetUtil;
|
||||
import org.dromara.hutool.core.map.TableMap;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.*;
|
||||
@@ -14,15 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation.elements;
|
||||
package org.dromara.hutool.core.annotation.elements;
|
||||
|
||||
import cn.hutool.v7.core.annotation.AnnotationUtil;
|
||||
import cn.hutool.v7.core.collection.CollUtil;
|
||||
import cn.hutool.v7.core.collection.set.SetUtil;
|
||||
import cn.hutool.v7.core.reflect.ClassUtil;
|
||||
import cn.hutool.v7.core.reflect.method.MethodUtil;
|
||||
import cn.hutool.v7.core.text.CharSequenceUtil;
|
||||
import cn.hutool.v7.core.array.ArrayUtil;
|
||||
import org.dromara.hutool.core.annotation.AnnotationUtil;
|
||||
import org.dromara.hutool.core.collection.CollUtil;
|
||||
import org.dromara.hutool.core.collection.set.SetUtil;
|
||||
import org.dromara.hutool.core.reflect.ClassUtil;
|
||||
import org.dromara.hutool.core.reflect.method.MethodUtil;
|
||||
import org.dromara.hutool.core.text.CharSequenceUtil;
|
||||
import org.dromara.hutool.core.array.ArrayUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
@@ -14,15 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation.elements;
|
||||
package org.dromara.hutool.core.annotation.elements;
|
||||
|
||||
import cn.hutool.v7.core.annotation.AnnotationMapping;
|
||||
import cn.hutool.v7.core.annotation.AnnotationUtil;
|
||||
import cn.hutool.v7.core.annotation.ResolvedAnnotationMapping;
|
||||
import cn.hutool.v7.core.stream.EasyStream;
|
||||
import cn.hutool.v7.core.text.CharSequenceUtil;
|
||||
import cn.hutool.v7.core.array.ArrayUtil;
|
||||
import cn.hutool.v7.core.util.ObjUtil;
|
||||
import org.dromara.hutool.core.annotation.AnnotationMapping;
|
||||
import org.dromara.hutool.core.annotation.AnnotationUtil;
|
||||
import org.dromara.hutool.core.annotation.ResolvedAnnotationMapping;
|
||||
import org.dromara.hutool.core.stream.EasyStream;
|
||||
import org.dromara.hutool.core.text.CharSequenceUtil;
|
||||
import org.dromara.hutool.core.array.ArrayUtil;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Inherited;
|
||||
@@ -14,14 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.annotation.elements;
|
||||
package org.dromara.hutool.core.annotation.elements;
|
||||
|
||||
import cn.hutool.v7.core.annotation.AnnotationMapping;
|
||||
import cn.hutool.v7.core.annotation.AnnotationUtil;
|
||||
import cn.hutool.v7.core.annotation.RepeatableAnnotationCollector;
|
||||
import cn.hutool.v7.core.collection.CollUtil;
|
||||
import cn.hutool.v7.core.text.CharSequenceUtil;
|
||||
import cn.hutool.v7.core.array.ArrayUtil;
|
||||
import org.dromara.hutool.core.annotation.AnnotationMapping;
|
||||
import org.dromara.hutool.core.annotation.AnnotationUtil;
|
||||
import org.dromara.hutool.core.annotation.RepeatableAnnotationCollector;
|
||||
import org.dromara.hutool.core.collection.CollUtil;
|
||||
import org.dromara.hutool.core.text.CharSequenceUtil;
|
||||
import org.dromara.hutool.core.array.ArrayUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
@@ -19,4 +19,4 @@
|
||||
*
|
||||
* @author Looly, huangchengxing
|
||||
*/
|
||||
package cn.hutool.v7.core.annotation.elements;
|
||||
package org.dromara.hutool.core.annotation.elements;
|
||||
@@ -26,4 +26,4 @@
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.v7.core.annotation;
|
||||
package org.dromara.hutool.core.annotation;
|
||||
@@ -14,21 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.array;
|
||||
package org.dromara.hutool.core.array;
|
||||
|
||||
import cn.hutool.v7.core.collection.ListUtil;
|
||||
import cn.hutool.v7.core.collection.set.SetUtil;
|
||||
import cn.hutool.v7.core.collection.set.UniqueKeySet;
|
||||
import cn.hutool.v7.core.comparator.CompareUtil;
|
||||
import cn.hutool.v7.core.convert.ConvertUtil;
|
||||
import cn.hutool.v7.core.exception.ExceptionUtil;
|
||||
import cn.hutool.v7.core.exception.HutoolException;
|
||||
import cn.hutool.v7.core.lang.Assert;
|
||||
import cn.hutool.v7.core.map.MapUtil;
|
||||
import cn.hutool.v7.core.text.StrJoiner;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import cn.hutool.v7.core.util.ObjUtil;
|
||||
import cn.hutool.v7.core.util.RandomUtil;
|
||||
import org.dromara.hutool.core.collection.ListUtil;
|
||||
import org.dromara.hutool.core.collection.set.SetUtil;
|
||||
import org.dromara.hutool.core.collection.set.UniqueKeySet;
|
||||
import org.dromara.hutool.core.comparator.CompareUtil;
|
||||
import org.dromara.hutool.core.convert.ConvertUtil;
|
||||
import org.dromara.hutool.core.exception.ExceptionUtil;
|
||||
import org.dromara.hutool.core.exception.HutoolException;
|
||||
import org.dromara.hutool.core.lang.Assert;
|
||||
import org.dromara.hutool.core.map.MapUtil;
|
||||
import org.dromara.hutool.core.text.StrJoiner;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
import org.dromara.hutool.core.util.RandomUtil;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
@@ -14,15 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.array;
|
||||
package org.dromara.hutool.core.array;
|
||||
|
||||
import cn.hutool.v7.core.collection.iter.ArrayIter;
|
||||
import cn.hutool.v7.core.convert.ConvertUtil;
|
||||
import cn.hutool.v7.core.lang.wrapper.Wrapper;
|
||||
import cn.hutool.v7.core.lang.Assert;
|
||||
import cn.hutool.v7.core.lang.Validator;
|
||||
import cn.hutool.v7.core.reflect.ClassUtil;
|
||||
import cn.hutool.v7.core.util.ObjUtil;
|
||||
import org.dromara.hutool.core.collection.iter.ArrayIter;
|
||||
import org.dromara.hutool.core.convert.ConvertUtil;
|
||||
import org.dromara.hutool.core.lang.wrapper.Wrapper;
|
||||
import org.dromara.hutool.core.lang.Assert;
|
||||
import org.dromara.hutool.core.lang.Validator;
|
||||
import org.dromara.hutool.core.reflect.ClassUtil;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
@@ -14,12 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.array;
|
||||
package org.dromara.hutool.core.array;
|
||||
|
||||
import cn.hutool.v7.core.lang.Assert;
|
||||
import cn.hutool.v7.core.math.NumberUtil;
|
||||
import cn.hutool.v7.core.util.ObjUtil;
|
||||
import cn.hutool.v7.core.util.RandomUtil;
|
||||
import org.dromara.hutool.core.lang.Assert;
|
||||
import org.dromara.hutool.core.math.NumberUtil;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
import org.dromara.hutool.core.util.RandomUtil;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
@@ -19,4 +19,4 @@
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
package cn.hutool.v7.core.array;
|
||||
package org.dromara.hutool.core.array;
|
||||
@@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.core.bean;
|
||||
package org.dromara.hutool.core.bean;
|
||||
|
||||
import cn.hutool.v7.core.reflect.Invoker;
|
||||
import org.dromara.hutool.core.reflect.Invoker;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user