mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2025-01-31 09:30:05 +08:00
【代码新增】AI:开启 AI 租户的能力
This commit is contained in:
parent
c98495dfcb
commit
2e73959a2c
@ -32,6 +32,11 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Web 相关 -->
|
<!-- Web 相关 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
POST {{baseUrl}}/ai/chat/message/send
|
POST {{baseUrl}}/ai/chat/message/send
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Authorization: {{token}}
|
Authorization: {{token}}
|
||||||
|
tenant-id: {{adminTenentId}}
|
||||||
|
|
||||||
{
|
{
|
||||||
"conversationId": "1781604279872581724",
|
"conversationId": "1781604279872581724",
|
||||||
@ -12,9 +13,10 @@ Authorization: {{token}}
|
|||||||
POST {{baseUrl}}/ai/chat/message/send-stream
|
POST {{baseUrl}}/ai/chat/message/send-stream
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Authorization: {{token}}
|
Authorization: {{token}}
|
||||||
|
tenant-id: {{adminTenentId}}
|
||||||
|
|
||||||
{
|
{
|
||||||
"conversationId": "1781604279872581690",
|
"conversationId": "1781604279872581724",
|
||||||
"content": "1+1=?"
|
"content": "1+1=?"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +57,4 @@ public class AiImageRespVO {
|
|||||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,11 @@ public class AiImageDO extends BaseDO {
|
|||||||
*/
|
*/
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime finishTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 绘画错误信息
|
* 绘画错误信息
|
||||||
*/
|
*/
|
||||||
@ -112,11 +117,6 @@ public class AiImageDO extends BaseDO {
|
|||||||
*/
|
*/
|
||||||
private String taskId;
|
private String taskId;
|
||||||
|
|
||||||
/**
|
|
||||||
* 完成时间
|
|
||||||
*/
|
|
||||||
private LocalDateTime finishTime;
|
|
||||||
|
|
||||||
public static class ButtonTypeHandler extends AbstractJsonTypeHandler<Object> {
|
public static class ButtonTypeHandler extends AbstractJsonTypeHandler<Object> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.ai.core.util.AiUtils;
|
|||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
|
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
|
||||||
@ -125,11 +126,14 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
|||||||
return success(new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
|
return success(new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
|
||||||
.setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent)));
|
.setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent)));
|
||||||
}).doOnComplete(() -> {
|
}).doOnComplete(() -> {
|
||||||
chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString()));
|
// 忽略租户,因为 Flux 异步无法透传租户
|
||||||
|
TenantUtils.executeIgnore(() ->
|
||||||
|
chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString())));
|
||||||
}).doOnError(throwable -> {
|
}).doOnError(throwable -> {
|
||||||
// TODO @芋艿:失败的情况下,要不要删除消息
|
|
||||||
log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable);
|
log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable);
|
||||||
chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage()));
|
// 忽略租户,因为 Flux 异步无法透传租户
|
||||||
|
TenantUtils.executeIgnore(() ->
|
||||||
|
chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage())));
|
||||||
}).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR)));
|
}).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
|||||||
import cn.iocoder.yudao.framework.ai.core.util.AiUtils;
|
import cn.iocoder.yudao.framework.ai.core.util.AiUtils;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
|
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
|
||||||
@ -75,10 +76,14 @@ public class AiWriteServiceImpl implements AiWriteService {
|
|||||||
// 响应结果
|
// 响应结果
|
||||||
return success(newContent);
|
return success(newContent);
|
||||||
}).doOnComplete(() -> {
|
}).doOnComplete(() -> {
|
||||||
writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setGeneratedContent(contentBuffer.toString()));
|
// 忽略租户,因为 Flux 异步无法透传租户
|
||||||
|
TenantUtils.executeIgnore(() ->
|
||||||
|
writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setGeneratedContent(contentBuffer.toString())));
|
||||||
}).doOnError(throwable -> {
|
}).doOnError(throwable -> {
|
||||||
log.error("[AI Write][generateReqVO({}) 发生异常]", generateReqVO, throwable);
|
log.error("[generateWriteContent][generateReqVO({}) 发生异常]", generateReqVO, throwable);
|
||||||
writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setErrorMessage(throwable.getMessage()));
|
// 忽略租户,因为 Flux 异步无法透传租户
|
||||||
|
TenantUtils.executeIgnore(() ->
|
||||||
|
writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setErrorMessage(throwable.getMessage())));
|
||||||
}).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));
|
}).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ public class AiClientFactoryImpl implements AiClientFactory {
|
|||||||
* 可参考 {@link QianFanAutoConfiguration#qianFanChatModel(QianFanConnectionProperties, QianFanChatProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)}
|
* 可参考 {@link QianFanAutoConfiguration#qianFanChatModel(QianFanConnectionProperties, QianFanChatProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)}
|
||||||
*/
|
*/
|
||||||
private static QianFanChatModel buildYiYanChatClient(String key) {
|
private static QianFanChatModel buildYiYanChatClient(String key) {
|
||||||
// TODO 芋艿:貌似目前设置,request 势必会报错
|
// TODO @xin:貌似目前设置,request 势必会报错;看看能不能有办法,参考 buildQianWenChatClient,调用 QianFanAutoConfiguration#qianFanChatModel初始化,当然 key 要用自己的哈
|
||||||
List<String> keys = StrUtil.split(key, '|');
|
List<String> keys = StrUtil.split(key, '|');
|
||||||
Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式");
|
Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式");
|
||||||
String appKey = keys.get(0);
|
String appKey = keys.get(0);
|
||||||
@ -191,8 +191,8 @@ public class AiClientFactoryImpl implements AiClientFactory {
|
|||||||
private static TongYiChatModel buildQianWenChatClient(String key) {
|
private static TongYiChatModel buildQianWenChatClient(String key) {
|
||||||
com.alibaba.dashscope.aigc.generation.Generation generation = SpringUtil.getBean(Generation.class);
|
com.alibaba.dashscope.aigc.generation.Generation generation = SpringUtil.getBean(Generation.class);
|
||||||
TongYiChatProperties chatOptions = SpringUtil.getBean(TongYiChatProperties.class);
|
TongYiChatProperties chatOptions = SpringUtil.getBean(TongYiChatProperties.class);
|
||||||
// TODO @芋艿:貌似 apiKey 是全局唯一的???得测试下
|
// TODO @xin:貌似 apiKey 是全局唯一的???得测试下
|
||||||
// TODO @芋艿:貌似阿里云不是增量返回的
|
// TODO @xin:貌似阿里云不是增量返回的
|
||||||
TongYiConnectionProperties connectionProperties = new TongYiConnectionProperties();
|
TongYiConnectionProperties connectionProperties = new TongYiConnectionProperties();
|
||||||
connectionProperties.setApiKey(key);
|
connectionProperties.setApiKey(key);
|
||||||
return new TongYiAutoConfiguration().tongYiChatClient(generation, chatOptions, connectionProperties);
|
return new TongYiAutoConfiguration().tongYiChatClient(generation, chatOptions, connectionProperties);
|
||||||
|
@ -70,12 +70,6 @@ spring:
|
|||||||
port: 6379 # 端口
|
port: 6379 # 端口
|
||||||
database: 0 # 数据库索引
|
database: 0 # 数据库索引
|
||||||
# password: dev # 密码,建议生产环境开启
|
# password: dev # 密码,建议生产环境开启
|
||||||
server:
|
|
||||||
servlet:
|
|
||||||
encoding:
|
|
||||||
enabled: true
|
|
||||||
charset: UTF-8
|
|
||||||
force: true
|
|
||||||
|
|
||||||
--- #################### 定时任务相关配置 ####################
|
--- #################### 定时任务相关配置 ####################
|
||||||
|
|
||||||
@ -226,8 +220,6 @@ yudao:
|
|||||||
enable: false
|
enable: false
|
||||||
demo: false # 关闭演示模式
|
demo: false # 关闭演示模式
|
||||||
tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc
|
tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc
|
||||||
tenant:
|
|
||||||
enable: false
|
|
||||||
|
|
||||||
justauth:
|
justauth:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
@ -34,6 +34,13 @@ spring:
|
|||||||
redis:
|
redis:
|
||||||
time-to-live: 1h # 设置过期时间为 1 小时
|
time-to-live: 1h # 设置过期时间为 1 小时
|
||||||
|
|
||||||
|
server:
|
||||||
|
servlet:
|
||||||
|
encoding:
|
||||||
|
enabled: true
|
||||||
|
charset: UTF-8 # 必须设置 UTF-8,避免 WebFlux 流式返回(AI 场景)会乱码问题
|
||||||
|
force: true
|
||||||
|
|
||||||
--- #################### 接口文档配置 ####################
|
--- #################### 接口文档配置 ####################
|
||||||
|
|
||||||
springdoc:
|
springdoc:
|
||||||
|
Loading…
Reference in New Issue
Block a user