Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro
# Conflicts: # yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
This commit is contained in:
commit
41640642d4
Binary file not shown.
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 25 KiB |
@ -11,7 +11,7 @@
|
|||||||
Target Server Version : 80200 (8.2.0)
|
Target Server Version : 80200 (8.2.0)
|
||||||
File Encoding : 65001
|
File Encoding : 65001
|
||||||
|
|
||||||
Date: 31/12/2024 09:16:18
|
Date: 14/03/2025 22:52:31
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SET NAMES utf8mb4;
|
SET NAMES utf8mb4;
|
||||||
@ -91,7 +91,7 @@ CREATE TABLE `infra_api_error_log` (
|
|||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 21226 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
|
) ENGINE = InnoDB AUTO_INCREMENT = 21417 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of infra_api_error_log
|
-- Records of infra_api_error_log
|
||||||
@ -128,7 +128,7 @@ CREATE TABLE `infra_codegen_column` (
|
|||||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 2483 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
|
) ENGINE = InnoDB AUTO_INCREMENT = 2538 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of infra_codegen_column
|
-- Records of infra_codegen_column
|
||||||
@ -166,7 +166,7 @@ CREATE TABLE `infra_codegen_table` (
|
|||||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 187 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
|
) ENGINE = InnoDB AUTO_INCREMENT = 191 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of infra_codegen_table
|
-- Records of infra_codegen_table
|
||||||
@ -250,7 +250,7 @@ CREATE TABLE `infra_file` (
|
|||||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 1577 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
|
) ENGINE = InnoDB AUTO_INCREMENT = 1655 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of infra_file
|
-- Records of infra_file
|
||||||
@ -444,7 +444,7 @@ CREATE TABLE `system_dict_data` (
|
|||||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 1683 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
|
) ENGINE = InnoDB AUTO_INCREMENT = 1694 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of system_dict_data
|
-- Records of system_dict_data
|
||||||
@ -831,7 +831,7 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
|
|||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1551, 2, '描述模式', '2', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:37', '1', '2024-06-28 01:22:24', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1551, 2, '描述模式', '2', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:37', '1', '2024-06-28 01:22:24', b'0');
|
||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1552, 8, 'Suno', 'Suno', 'ai_platform', 0, '', '', '', '1', '2024-06-29 09:13:36', '1', '2024-06-29 09:13:41', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1552, 8, 'Suno', 'Suno', 'ai_platform', 0, '', '', '', '1', '2024-06-29 09:13:36', '1', '2024-06-29 09:13:41', b'0');
|
||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1553, 9, 'DeepSeek', 'DeepSeek', 'ai_platform', 0, '', '', '', '1', '2024-07-06 12:04:30', '1', '2024-07-06 12:05:20', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1553, 9, 'DeepSeek', 'DeepSeek', 'ai_platform', 0, '', '', '', '1', '2024-07-06 12:04:30', '1', '2024-07-06 12:05:20', b'0');
|
||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1554, 10, '智谱', 'ZhiPu', 'ai_platform', 0, '', '', '', '1', '2024-07-06 18:00:35', '1', '2024-07-06 18:00:35', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1554, 13, '智谱', 'ZhiPu', 'ai_platform', 0, '', '', '', '1', '2024-07-06 18:00:35', '1', '2025-02-24 20:18:41', b'0');
|
||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1555, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1555, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', b'0');
|
||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1556, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1556, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', b'0');
|
||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1557, 6, '文章', '6', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:05', '1', '2024-07-07 15:50:05', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1557, 6, '文章', '6', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:05', '1', '2024-07-07 15:50:05', b'0');
|
||||||
@ -888,13 +888,23 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
|
|||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1673, 2, 'OPC UA', '2', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:31', '1', '2024-09-06 22:26:31', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1673, 2, 'OPC UA', '2', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:31', '1', '2024-09-06 22:26:31', b'0');
|
||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1674, 3, 'ZigBee', '3', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:39', '1', '2024-09-06 22:26:39', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1674, 3, 'ZigBee', '3', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:39', '1', '2024-09-06 22:26:39', b'0');
|
||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1675, 4, 'BLE', '4', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:48', '1', '2024-09-06 22:26:48', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1675, 4, 'BLE', '4', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:48', '1', '2024-09-06 22:26:48', b'0');
|
||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1676, 0, '未激活', '0', 'iot_device_status', 0, '', '', '', '1', '2024-09-21 08:13:34', '1', '2024-09-21 08:13:34', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1676, 0, '未激活', '0', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:34', '1', '2025-01-28 16:09:27', b'0');
|
||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1677, 1, '在线', '1', 'iot_device_status', 0, '', '', '', '1', '2024-09-21 08:13:48', '1', '2024-09-21 08:13:48', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1677, 1, '在线', '1', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:48', '1', '2025-01-28 16:09:26', b'0');
|
||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1678, 2, '离线', '2', 'iot_device_status', 0, '', '', '', '1', '2024-09-21 08:13:59', '1', '2024-09-21 08:13:59', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1678, 2, '离线', '2', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:59', '1', '2025-01-28 16:09:25', b'0');
|
||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1679, 3, '已禁用', '3', 'iot_device_status', 0, '', '', '', '1', '2024-09-21 08:14:13', '1', '2024-09-21 08:14:13', b'0');
|
|
||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1680, 1, '属性', '1', 'iot_product_function_type', 0, '', '', '', '1', '2024-09-29 20:03:01', '1', '2024-09-29 20:09:41', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1680, 1, '属性', '1', 'iot_product_function_type', 0, '', '', '', '1', '2024-09-29 20:03:01', '1', '2024-09-29 20:09:41', b'0');
|
||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1681, 2, '服务', '2', 'iot_product_function_type', 0, '', '', '', '1', '2024-09-29 20:03:11', '1', '2024-09-29 20:08:23', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1681, 2, '服务', '2', 'iot_product_function_type', 0, '', '', '', '1', '2024-09-29 20:03:11', '1', '2024-09-29 20:08:23', b'0');
|
||||||
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1682, 3, '事件', '3', 'iot_product_function_type', 0, '', '', '', '1', '2024-09-29 20:03:20', '1', '2024-09-29 20:08:20', b'0');
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1682, 3, '事件', '3', 'iot_product_function_type', 0, '', '', '', '1', '2024-09-29 20:03:20', '1', '2024-09-29 20:08:20', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1683, 10, '字节豆包', 'DouBao', 'ai_platform', 0, '', '', '', '1', '2025-02-23 19:51:40', '1', '2025-02-23 19:52:02', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1684, 11, '腾讯混元', 'HunYuan', 'ai_platform', 0, '', '', '', '1', '2025-02-23 20:58:04', '1', '2025-02-23 20:58:04', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1685, 12, '硅基流动', 'SiliconFlow', 'ai_platform', 0, '', '', '', '1', '2025-02-24 20:19:09', '1', '2025-02-24 20:19:09', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1686, 1, '聊天', '1', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:26:34', '1', '2025-03-03 12:26:34', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1687, 2, '图像', '2', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:23', '1', '2025-03-03 12:27:23', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1688, 3, '音频', '3', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:51', '1', '2025-03-03 12:27:51', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1689, 4, '视频', '4', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:03', '1', '2025-03-03 12:28:03', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1690, 5, '向量', '5', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:15', '1', '2025-03-03 12:28:15', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1691, 6, '重排', '6', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:26', '1', '2025-03-03 12:28:26', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1692, 14, 'MiniMax', 'MiniMax', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:04:51', '1', '2025-03-11 20:04:51', b'0');
|
||||||
|
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1693, 15, '月之暗灭', 'Moonshot', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:05:08', '1', '2025-03-11 20:05:08', b'0');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
@ -914,7 +924,7 @@ CREATE TABLE `system_dict_type` (
|
|||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
`deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
|
`deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 640 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
|
) ENGINE = InnoDB AUTO_INCREMENT = 641 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of system_dict_type
|
-- Records of system_dict_type
|
||||||
@ -1014,13 +1024,14 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat
|
|||||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (628, 'AI 写作类型', 'ai_write_type', 0, '', '1', '2024-07-10 21:25:29', '1', '2024-07-10 21:25:29', b'0', '1970-01-01 00:00:00');
|
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (628, 'AI 写作类型', 'ai_write_type', 0, '', '1', '2024-07-10 21:25:29', '1', '2024-07-10 21:25:29', b'0', '1970-01-01 00:00:00');
|
||||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (629, 'BPM 流程模型类型', 'bpm_model_type', 0, '', '1', '2024-08-26 15:21:43', '1', '2024-08-26 15:21:43', b'0', '1970-01-01 00:00:00');
|
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (629, 'BPM 流程模型类型', 'bpm_model_type', 0, '', '1', '2024-08-26 15:21:43', '1', '2024-08-26 15:21:43', b'0', '1970-01-01 00:00:00');
|
||||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (630, 'IOT 接入网关协议', 'iot_protocol_type', 0, '', '1', '2024-09-06 22:20:17', '1', '2024-09-06 22:20:17', b'0', '1970-01-01 00:00:00');
|
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (630, 'IOT 接入网关协议', 'iot_protocol_type', 0, '', '1', '2024-09-06 22:20:17', '1', '2024-09-06 22:20:17', b'0', '1970-01-01 00:00:00');
|
||||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (631, 'IOT 设备状态', 'iot_device_status', 0, '', '1', '2024-09-21 08:12:55', '1', '2024-09-21 08:12:55', b'0', '1970-01-01 00:00:00');
|
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (631, 'IOT 设备状态', 'iot_device_state', 0, '', '1', '2024-09-21 08:12:55', '1', '2025-01-28 16:09:42', b'0', '1970-01-01 00:00:00');
|
||||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (632, 'IOT 物模型功能类型', 'iot_product_function_type', 0, '', '1', '2024-09-29 20:02:36', '1', '2024-09-29 20:09:26', b'0', '1970-01-01 00:00:00');
|
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (632, 'IOT 物模型功能类型', 'iot_product_function_type', 0, '', '1', '2024-09-29 20:02:36', '1', '2024-09-29 20:09:26', b'0', '1970-01-01 00:00:00');
|
||||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (634, 'IOT 数据格式', 'iot_data_format', 0, '', '1', '2024-08-10 11:52:58', '1', '2024-09-06 14:30:14', b'0', '1970-01-01 00:00:00');
|
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (634, 'IOT 数据格式', 'iot_data_format', 0, '', '1', '2024-08-10 11:52:58', '1', '2024-09-06 14:30:14', b'0', '1970-01-01 00:00:00');
|
||||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (635, 'IOT 产品设备类型', 'iot_product_device_type', 0, '', '1', '2024-08-10 11:54:30', '1', '2024-08-10 04:06:56', b'0', '1970-01-01 00:00:00');
|
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (635, 'IOT 产品设备类型', 'iot_product_device_type', 0, '', '1', '2024-08-10 11:54:30', '1', '2024-08-10 04:06:56', b'0', '1970-01-01 00:00:00');
|
||||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (637, 'IOT 产品状态', 'iot_product_status', 0, '', '1', '2024-08-10 12:06:09', '1', '2024-08-10 12:06:09', b'0', '1970-01-01 00:00:00');
|
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (637, 'IOT 产品状态', 'iot_product_status', 0, '', '1', '2024-08-10 12:06:09', '1', '2024-08-10 12:06:09', b'0', '1970-01-01 00:00:00');
|
||||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (638, 'IOT 数据校验级别', 'iot_validate_type', 0, '', '1', '2024-09-06 20:05:13', '1', '2024-09-06 20:05:13', b'0', '1970-01-01 00:00:00');
|
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (638, 'IOT 数据校验级别', 'iot_validate_type', 0, '', '1', '2024-09-06 20:05:13', '1', '2024-09-06 20:05:13', b'0', '1970-01-01 00:00:00');
|
||||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (639, 'IOT 联网方式', 'iot_net_type', 0, '', '1', '2024-09-06 22:04:13', '1', '2024-09-06 22:04:13', b'0', '1970-01-01 00:00:00');
|
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (639, 'IOT 联网方式', 'iot_net_type', 0, '', '1', '2024-09-06 22:04:13', '1', '2024-09-06 22:04:13', b'0', '1970-01-01 00:00:00');
|
||||||
|
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (640, 'AI 模型类型', 'ai_model_type', 0, '', '1', '2025-03-03 12:24:07', '1', '2025-03-03 12:24:07', b'0', '1970-01-01 00:00:00');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
@ -1044,7 +1055,7 @@ CREATE TABLE `system_login_log` (
|
|||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 3415 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
|
) ENGINE = InnoDB AUTO_INCREMENT = 3442 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of system_login_log
|
-- Records of system_login_log
|
||||||
@ -1175,7 +1186,7 @@ CREATE TABLE `system_menu` (
|
|||||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 2913 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
|
) ENGINE = InnoDB AUTO_INCREMENT = 2925 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of system_menu
|
-- Records of system_menu
|
||||||
@ -1373,7 +1384,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
|
|||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1241, '文件配置删除', 'infra:file-config:delete', 3, 4, 1237, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1241, '文件配置删除', 'infra:file-config:delete', 3, 4, 1237, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1242, '文件配置导出', 'infra:file-config:export', 3, 5, 1237, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1242, '文件配置导出', 'infra:file-config:export', 3, 5, 1237, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1243, '文件管理', '', 2, 6, 2, 'file', 'ep:files', NULL, '', 0, b'1', b'1', b'1', '1', '2022-03-16 23:47:40', '1', '2024-04-23 00:02:11', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1243, '文件管理', '', 2, 6, 2, 'file', 'ep:files', NULL, '', 0, b'1', b'1', b'1', '1', '2022-03-16 23:47:40', '1', '2024-04-23 00:02:11', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1254, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'ep:avatar', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-04-23 01:03:15', '1', '2024-09-06 09:19:42', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1254, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'ep:avatar', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-04-23 01:03:15', '104', '2025-01-04 10:59:37', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1255, '数据源配置', '', 2, 1, 2, 'data-source-config', 'ep:data-analysis', 'infra/dataSourceConfig/index', 'InfraDataSourceConfig', 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '1', '2024-02-29 08:51:25', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1255, '数据源配置', '', 2, 1, 2, 'data-source-config', 'ep:data-analysis', 'infra/dataSourceConfig/index', 'InfraDataSourceConfig', 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '1', '2024-02-29 08:51:25', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1256, '数据源配置查询', 'infra:data-source-config:query', 3, 1, 1255, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1256, '数据源配置查询', 'infra:data-source-config:query', 3, 1, 1255, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1257, '数据源配置创建', 'infra:data-source-config:create', 3, 2, 1255, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1257, '数据源配置创建', 'infra:data-source-config:create', 3, 2, 1255, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', b'0');
|
||||||
@ -1978,11 +1989,11 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
|
|||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2763, 'API 密钥创建', 'ai:api-key:create', 3, 2, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:26', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2763, 'API 密钥创建', 'ai:api-key:create', 3, 2, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:26', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2764, 'API 密钥更新', 'ai:api-key:update', 3, 3, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:42', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2764, 'API 密钥更新', 'ai:api-key:update', 3, 3, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:42', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2765, 'API 密钥删除', 'ai:api-key:delete', 3, 4, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:48', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2765, 'API 密钥删除', 'ai:api-key:delete', 3, 4, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:48', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2767, '聊天模型', '', 2, 0, 2760, 'chat-model', 'fa-solid:abacus', 'ai/model/chatModel/index.vue', 'AiChatModel', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2024-05-10 22:44:16', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2767, '模型配置', '', 2, 0, 2760, 'model', 'fa-solid:abacus', 'ai/model/model/index.vue', 'AiModel', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:57:41', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2768, '聊天模型查询', 'ai:chat-model:query', 3, 1, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:02', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2768, '聊天模型查询', 'ai:model:query', 3, 1, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:19:46', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2769, '聊天模型创建', 'ai:chat-model:create', 3, 2, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:12', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2769, '聊天模型创建', 'ai:model:create', 3, 2, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:10', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2770, '聊天模型更新', 'ai:chat-model:update', 3, 3, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:18', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2770, '聊天模型更新', 'ai:model:update', 3, 3, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:14', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2771, '聊天模型删除', 'ai:chat-model:delete', 3, 4, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:23', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2771, '聊天模型删除', 'ai:model:delete', 3, 4, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:27', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2773, '聊天角色', '', 2, 0, 2760, 'chat-role', 'fa:user-secret', 'ai/model/chatRole/index.vue', 'AiChatRole', 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '1', '2024-05-13 20:41:45', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2773, '聊天角色', '', 2, 0, 2760, 'chat-role', 'fa:user-secret', 'ai/model/chatRole/index.vue', 'AiChatRole', 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '1', '2024-05-13 20:41:45', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2774, '聊天角色查询', 'ai:chat-role:query', 3, 1, 2773, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2774, '聊天角色查询', 'ai:chat-role:query', 3, 1, 2773, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2775, '聊天角色创建', 'ai:chat-role:create', 3, 2, 2773, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2775, '聊天角色创建', 'ai:chat-role:create', 3, 2, 2773, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', b'0');
|
||||||
@ -2008,7 +2019,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
|
|||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2795, 'AI 写作删除', 'ai:write:delete', 3, 4, 2793, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2795, 'AI 写作删除', 'ai:write:delete', 3, 4, 2793, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2796, 'AI 音乐', '', 2, 4, 2758, 'music', 'fa:music', 'ai/music/index/index.vue', 'AiMusic', 0, b'1', b'1', b'1', '1', '2024-07-17 09:21:12', '1', '2024-07-29 21:11:52', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2796, 'AI 音乐', '', 2, 4, 2758, 'music', 'fa:music', 'ai/music/index/index.vue', 'AiMusic', 0, b'1', b'1', b'1', '1', '2024-07-17 09:21:12', '1', '2024-07-29 21:11:52', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2797, '客服中心', '', 2, 100, 2362, 'kefu', 'fa-solid:user-alt', 'mall/promotion/kefu/index', 'KeFu', 0, b'1', b'1', b'1', '1', '2024-07-17 23:49:05', '1', '2024-07-17 23:49:16', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2797, '客服中心', '', 2, 100, 2362, 'kefu', 'fa-solid:user-alt', 'mall/promotion/kefu/index', 'KeFu', 0, b'1', b'1', b'1', '1', '2024-07-17 23:49:05', '1', '2024-07-17 23:49:16', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2798, 'AI 思维导图', '', 2, 5, 2758, 'mind-map', 'fa:sitemap', 'ai/mindmap/index/index.vue', 'AiMindMap', 0, b'1', b'1', b'1', '1', '2024-07-29 21:31:59', '1', '2024-07-29 21:33:20', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2798, 'AI 思维导图', '', 2, 6, 2758, 'mind-map', 'fa:sitemap', 'ai/mindmap/index/index.vue', 'AiMindMap', 0, b'1', b'1', b'1', '1', '2024-07-29 21:31:59', '1', '2025-03-02 18:57:31', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2799, '导图管理', '', 2, 14, 2760, 'mind-map', 'fa:map', 'ai/mindmap/manager/index', 'AiMindMapManager', 0, b'1', b'1', b'1', '', '2024-08-10 09:15:09', '1', '2024-08-10 17:24:28', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2799, '导图管理', '', 2, 14, 2760, 'mind-map', 'fa:map', 'ai/mindmap/manager/index', 'AiMindMapManager', 0, b'1', b'1', b'1', '', '2024-08-10 09:15:09', '1', '2024-08-10 17:24:28', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2800, '思维导图查询', 'ai:mind-map:query', 3, 1, 2799, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2800, '思维导图查询', 'ai:mind-map:query', 3, 1, 2799, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2801, '思维导图删除', 'ai:mind-map:delete', 3, 4, 2799, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2801, '思维导图删除', 'ai:mind-map:delete', 3, 4, 2799, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', b'0');
|
||||||
@ -2045,6 +2056,18 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
|
|||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2910, 'IoT 产品物模型删除', 'iot:think-model-function:delete', 3, 4, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2910, 'IoT 产品物模型删除', 'iot:think-model-function:delete', 3, 4, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2911, 'IoT 产品物模型导出', 'iot:think-model-function:export', 3, 5, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2911, 'IoT 产品物模型导出', 'iot:think-model-function:export', 3, 5, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0');
|
||||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2912, '创建推广员', 'trade:brokerage-user:create', 3, 7, 2346, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-12-01 14:32:39', '1', '2024-12-01 14:32:39', b'0');
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2912, '创建推广员', 'trade:brokerage-user:create', 3, 7, 2346, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-12-01 14:32:39', '1', '2024-12-01 14:32:39', b'0');
|
||||||
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2913, '流程清理', 'bpm:model:clean', 3, 7, 1193, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-17 19:32:06', '1', '2025-01-17 19:32:06', b'0');
|
||||||
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2914, '积分商城活动关闭', 'promotion:point-activity:close', 3, 6, 2808, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-23 20:23:34', '1', '2025-01-23 20:23:34', b'0');
|
||||||
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2915, 'AI 知识库', '', 2, 5, 2758, 'knowledge', 'ep:notebook', 'ai/knowledge/knowledge/index', 'AiKnowledge', 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '1', '2025-03-02 18:58:37', b'0');
|
||||||
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2916, 'AI 知识库查询', 'ai:knowledge:query', 3, 1, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0');
|
||||||
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2917, 'AI 知识库创建', 'ai:knowledge:create', 3, 2, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0');
|
||||||
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2918, 'AI 知识库更新', 'ai:knowledge:update', 3, 3, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0');
|
||||||
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2919, 'AI 知识库删除', 'ai:knowledge:delete', 3, 4, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0');
|
||||||
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2920, '工具管理', '', 2, 0, 2760, 'tool', 'fa-solid:tools', 'ai/model/tool/index.vue', 'AiTool', 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '1', '2025-03-14 19:20:18', b'0');
|
||||||
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2921, '工具查询', 'ai:tool:query', 3, 1, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0');
|
||||||
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2922, '工具创建', 'ai:tool:create', 3, 2, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0');
|
||||||
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2923, '工具更新', 'ai:tool:update', 3, 3, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0');
|
||||||
|
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2924, '工具删除', 'ai:tool:delete', 3, 4, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
@ -2166,7 +2189,7 @@ CREATE TABLE `system_oauth2_access_token` (
|
|||||||
PRIMARY KEY (`id`) USING BTREE,
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
INDEX `idx_access_token`(`access_token` ASC) USING BTREE,
|
INDEX `idx_access_token`(`access_token` ASC) USING BTREE,
|
||||||
INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE
|
INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 12055 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
|
) ENGINE = InnoDB AUTO_INCREMENT = 13666 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of system_oauth2_access_token
|
-- Records of system_oauth2_access_token
|
||||||
@ -2288,7 +2311,7 @@ CREATE TABLE `system_oauth2_refresh_token` (
|
|||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 1711 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
|
) ENGINE = InnoDB AUTO_INCREMENT = 1732 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of system_oauth2_refresh_token
|
-- Records of system_oauth2_refresh_token
|
||||||
@ -2322,7 +2345,7 @@ CREATE TABLE `system_operate_log` (
|
|||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 9064 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本';
|
) ENGINE = InnoDB AUTO_INCREMENT = 9065 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of system_operate_log
|
-- Records of system_operate_log
|
||||||
@ -2783,9 +2806,7 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
|
|||||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2139, 2, 1011, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2139, 2, 1011, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
||||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2140, 2, 1012, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2140, 2, 1012, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
||||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2141, 2, 1013, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2141, 2, 1013, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
||||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2142, 2, 1014, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
|
||||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2143, 2, 1015, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2143, 2, 1015, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
||||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2144, 2, 1016, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
|
||||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2145, 2, 1017, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2145, 2, 1017, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
||||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2146, 2, 1018, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2146, 2, 1018, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
||||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2147, 2, 1019, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2147, 2, 1019, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
|
||||||
@ -3305,7 +3326,7 @@ CREATE TABLE `system_sms_code` (
|
|||||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||||
PRIMARY KEY (`id`) USING BTREE,
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号'
|
INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号'
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 645 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
|
) ENGINE = InnoDB AUTO_INCREMENT = 646 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of system_sms_code
|
-- Records of system_sms_code
|
||||||
@ -3346,7 +3367,7 @@ CREATE TABLE `system_sms_log` (
|
|||||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 1241 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
|
) ENGINE = InnoDB AUTO_INCREMENT = 1255 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of system_sms_log
|
-- Records of system_sms_log
|
||||||
@ -3588,7 +3609,7 @@ CREATE TABLE `system_user_role` (
|
|||||||
`deleted` bit(1) NULL DEFAULT b'0' COMMENT '是否删除',
|
`deleted` bit(1) NULL DEFAULT b'0' COMMENT '是否删除',
|
||||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 47 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户和角色关联表';
|
) ENGINE = InnoDB AUTO_INCREMENT = 48 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户和角色关联表';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of system_user_role
|
-- Records of system_user_role
|
||||||
@ -3604,12 +3625,12 @@ INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_t
|
|||||||
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (15, 111, 110, '110', '2022-02-23 13:14:38', '110', '2022-02-23 13:14:38', b'0', 121);
|
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (15, 111, 110, '110', '2022-02-23 13:14:38', '110', '2022-02-23 13:14:38', b'0', 121);
|
||||||
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (16, 113, 111, '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', b'0', 122);
|
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (16, 113, 111, '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', b'0', 122);
|
||||||
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (18, 1, 2, '1', '2022-05-12 20:39:29', '1', '2022-05-12 20:39:29', b'0', 1);
|
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (18, 1, 2, '1', '2022-05-12 20:39:29', '1', '2022-05-12 20:39:29', b'0', 1);
|
||||||
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (20, 104, 101, '1', '2022-05-28 15:43:57', '1', '2022-05-28 15:43:57', b'0', 1);
|
|
||||||
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (22, 115, 2, '1', '2022-07-21 22:08:30', '1', '2022-07-21 22:08:30', b'0', 1);
|
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (22, 115, 2, '1', '2022-07-21 22:08:30', '1', '2022-07-21 22:08:30', b'0', 1);
|
||||||
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (35, 112, 1, '1', '2024-03-15 20:00:24', '1', '2024-03-15 20:00:24', b'0', 1);
|
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (35, 112, 1, '1', '2024-03-15 20:00:24', '1', '2024-03-15 20:00:24', b'0', 1);
|
||||||
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (36, 118, 1, '1', '2024-03-17 09:12:08', '1', '2024-03-17 09:12:08', b'0', 1);
|
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (36, 118, 1, '1', '2024-03-17 09:12:08', '1', '2024-03-17 09:12:08', b'0', 1);
|
||||||
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (38, 114, 101, '1', '2024-03-24 22:23:03', '1', '2024-03-24 22:23:03', b'0', 1);
|
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (38, 114, 101, '1', '2024-03-24 22:23:03', '1', '2024-03-24 22:23:03', b'0', 1);
|
||||||
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (46, 117, 1, '1', '2024-10-02 10:16:11', '1', '2024-10-02 10:16:11', b'0', 1);
|
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (46, 117, 1, '1', '2024-10-02 10:16:11', '1', '2024-10-02 10:16:11', b'0', 1);
|
||||||
|
INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (47, 104, 2, '1', '2025-01-04 10:40:33', '1', '2025-01-04 10:40:33', b'0', 1);
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
@ -3644,10 +3665,10 @@ CREATE TABLE `system_users` (
|
|||||||
-- Records of system_users
|
-- Records of system_users
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
BEGIN;
|
BEGIN;
|
||||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1,2]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/bf2002b38950c904243be7c825d3f82e29f25a44526583c3fde2ebdff3a87f75.png', 0, '0:0:0:0:0:0:0:1', '2024-12-28 20:29:58', 'admin', '2021-01-05 17:03:47', NULL, '2024-12-28 20:29:58', b'0', 1);
|
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1,2]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/bf2002b38950c904243be7c825d3f82e29f25a44526583c3fde2ebdff3a87f75.png', 0, '0:0:0:0:0:0:0:1', '2025-03-13 12:44:59', 'admin', '2021-01-05 17:03:47', NULL, '2025-03-13 12:44:59', b'0', 1);
|
||||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$04$IgUse/ibRzAZ3rngCThmtemJeoh15Ux1TQ2hIMe4iwt/K3LcFHEda', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-11-02 14:00:46', '', '2021-01-07 09:07:17', NULL, '2024-11-02 14:00:46', b'0', 1);
|
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$04$IgUse/ibRzAZ3rngCThmtemJeoh15Ux1TQ2hIMe4iwt/K3LcFHEda', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-11-02 14:00:46', '', '2021-01-07 09:07:17', NULL, '2024-11-02 14:00:46', b'0', 1);
|
||||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$04$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', NULL, '2024-08-11 17:48:12', b'0', 1);
|
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$04$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', NULL, '2024-08-11 17:48:12', b'0', 1);
|
||||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$04$jDFLttgfik0QqJKAbfhMa.2A9xXoZmAIxakdFJUzkX.MgBKT6ddo6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-09-17 15:05:43', '', '2021-01-21 02:13:53', NULL, '2024-09-17 15:05:43', b'0', 1);
|
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$04$BrwaYn303hjA/6TnXqdGoOLhyHOAA0bVrAFu6.1dJKycqKUnIoRz2', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2025-01-04 10:40:49', '', '2021-01-21 02:13:53', NULL, '2025-01-04 10:40:49', b'0', 1);
|
||||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (107, 'admin107', '$2a$10$dYOOBKMO93v/.ReCqzyFg.o67Tqk.bbc2bhrpyBGkIw9aypCtr2pm', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 22:59:33', '1', '2022-02-27 08:26:51', b'0', 118);
|
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (107, 'admin107', '$2a$10$dYOOBKMO93v/.ReCqzyFg.o67Tqk.bbc2bhrpyBGkIw9aypCtr2pm', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 22:59:33', '1', '2022-02-27 08:26:51', b'0', 118);
|
||||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (108, 'admin108', '$2a$10$y6mfvKoNYL1GXWak8nYwVOH.kCWqjactkzdoIDgiKl93WN3Ejg.Lu', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 23:00:50', '1', '2022-02-27 08:26:53', b'0', 119);
|
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (108, 'admin108', '$2a$10$y6mfvKoNYL1GXWak8nYwVOH.kCWqjactkzdoIDgiKl93WN3Ejg.Lu', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 23:00:50', '1', '2022-02-27 08:26:53', b'0', 119);
|
||||||
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (109, 'admin109', '$2a$10$JAqvH0tEc0I7dfDVBI7zyuB4E3j.uH6daIjV53.vUS6PknFkDJkuK', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 23:11:50', '1', '2022-02-27 08:26:56', b'0', 120);
|
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (109, 'admin109', '$2a$10$JAqvH0tEc0I7dfDVBI7zyuB4E3j.uH6daIjV53.vUS6PknFkDJkuK', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 23:11:50', '1', '2022-02-27 08:26:56', b'0', 120);
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
<transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
|
<transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
|
||||||
<commons-net.version>3.11.1</commons-net.version>
|
<commons-net.version>3.11.1</commons-net.version>
|
||||||
<jsch.version>0.1.55</jsch.version>
|
<jsch.version>0.1.55</jsch.version>
|
||||||
<tika-core.version>2.9.2</tika-core.version>
|
<tika-core.version>3.1.0</tika-core.version> <!-- TODO 芋艿:升级后,需要看看 jdk8 的兼容性;也要同步 cloud -->
|
||||||
<ip2region.version>2.7.0</ip2region.version>
|
<ip2region.version>2.7.0</ip2region.version>
|
||||||
<bizlog-sdk.version>3.0.6</bizlog-sdk.version>
|
<bizlog-sdk.version>3.0.6</bizlog-sdk.version>
|
||||||
<netty.version>4.1.116.Final</netty.version>
|
<netty.version>4.1.116.Final</netty.version>
|
||||||
|
@ -30,6 +30,7 @@ import org.springframework.web.util.pattern.PathPattern;
|
|||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.annotation.security.PermitAll;
|
import javax.annotation.security.PermitAll;
|
||||||
|
import javax.servlet.DispatcherType;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -142,7 +143,9 @@ public class YudaoWebSecurityConfigurerAdapter {
|
|||||||
// ②:每个项目的自定义规则
|
// ②:每个项目的自定义规则
|
||||||
.authorizeHttpRequests(c -> authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(c)))
|
.authorizeHttpRequests(c -> authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(c)))
|
||||||
// ③:兜底规则,必须认证
|
// ③:兜底规则,必须认证
|
||||||
.authorizeHttpRequests(c -> c.anyRequest().authenticated());
|
.authorizeHttpRequests(c -> c
|
||||||
|
.dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll() // WebFlux 异步请求,无需认证,目的:SSE 场景
|
||||||
|
.anyRequest().authenticated());
|
||||||
|
|
||||||
// 添加 Token Filter
|
// 添加 Token Filter
|
||||||
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
@ -35,11 +35,7 @@ public enum AiChatRoleEnum {
|
|||||||
### 微信
|
### 微信
|
||||||
除此之外不要任何解释性语句。
|
除此之外不要任何解释性语句。
|
||||||
"""),
|
"""),
|
||||||
|
;
|
||||||
AI_KNOWLEDGE_ROLE("知识库助手", """
|
|
||||||
给你提供一些数据参考:{info},请回答我的问题。
|
|
||||||
请你跟进数据参考与工具返回结果回复用户的请求。
|
|
||||||
""");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 角色名
|
* 角色名
|
||||||
|
@ -12,49 +12,53 @@ public interface ErrorCodeConstants {
|
|||||||
// ========== API 密钥 1-040-000-000 ==========
|
// ========== API 密钥 1-040-000-000 ==========
|
||||||
ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "API 密钥不存在");
|
ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "API 密钥不存在");
|
||||||
ErrorCode API_KEY_DISABLE = new ErrorCode(1_040_000_001, "API 密钥已禁用!");
|
ErrorCode API_KEY_DISABLE = new ErrorCode(1_040_000_001, "API 密钥已禁用!");
|
||||||
ErrorCode API_KEY_MIDJOURNEY_NOT_FOUND = new ErrorCode(1_040_000_900, "Midjourney 模型不存在");
|
|
||||||
ErrorCode API_KEY_SUNO_NOT_FOUND = new ErrorCode(1_040_000_901, "Suno 模型不存在");
|
|
||||||
ErrorCode API_KEY_IMAGE_NODE_FOUND = new ErrorCode(1_040_000_902, "平台({}) 图片模型未配置");
|
|
||||||
|
|
||||||
// ========== API 聊天模型 1-040-001-000 ==========
|
// ========== API 模型 1-040-001-000 ==========
|
||||||
ErrorCode CHAT_MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!");
|
ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!");
|
||||||
ErrorCode CHAT_MODEL_DISABLE = new ErrorCode(1_040_001_001, "模型({})已禁用!");
|
ErrorCode MODEL_DISABLE = new ErrorCode(1_040_001_001, "模型({})已禁用!");
|
||||||
ErrorCode CHAT_MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认聊天模型");
|
ErrorCode MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认模型");
|
||||||
|
ErrorCode MODEL_USE_TYPE_ERROR = new ErrorCode(1_040_001_003, "操作失败,该模型的模型类型不正确");
|
||||||
|
|
||||||
// ========== API 聊天模型 1-040-002-000 ==========
|
// ========== API 聊天角色 1-040-002-000 ==========
|
||||||
ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "聊天角色不存在");
|
ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "聊天角色不存在");
|
||||||
ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, "聊天角色({})已禁用!");
|
ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, "聊天角色({})已禁用!");
|
||||||
|
|
||||||
// ========== API 聊天会话 1-040-003-000 ==========
|
// ========== API 聊天会话 1-040-003-000 ==========
|
||||||
|
|
||||||
ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "对话不存在!");
|
ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "对话不存在!");
|
||||||
ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, "操作失败,该聊天模型的配置不完整");
|
ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, "操作失败,该聊天模型的配置不完整");
|
||||||
|
|
||||||
// ========== API 聊天消息 1-040-004-000 ==========
|
// ========== API 聊天消息 1-040-004-000 ==========
|
||||||
|
|
||||||
ErrorCode CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, "消息不存在!");
|
ErrorCode CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, "消息不存在!");
|
||||||
ErrorCode CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "对话生成异常!");
|
ErrorCode CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "对话生成异常!");
|
||||||
|
|
||||||
// ========== API 绘画 1-040-005-000 ==========
|
// ========== API 绘画 1-040-005-000 ==========
|
||||||
|
ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_040_005_000, "图片不存在!");
|
||||||
ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_022_005_000, "图片不存在!");
|
ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_040_005_001, "Midjourney 提交失败!原因:{}");
|
||||||
ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_022_005_001, "Midjourney 提交失败!原因:{}");
|
ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_040_005_002, "Midjourney 按钮 customId 不存在! {}");
|
||||||
ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_022_005_002, "Midjourney 按钮 customId 不存在! {}");
|
|
||||||
ErrorCode IMAGE_FAIL = new ErrorCode(1_022_005_002, "图片绘画失败! {}");
|
|
||||||
|
|
||||||
// ========== API 音乐 1-040-006-000 ==========
|
// ========== API 音乐 1-040-006-000 ==========
|
||||||
ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_022_006_000, "音乐不存在!");
|
ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_040_006_000, "音乐不存在!");
|
||||||
|
|
||||||
// ========== API 写作 1-022-007-000 ==========
|
// ========== API 写作 1-040-007-000 ==========
|
||||||
ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_022_007_000, "作文不存在!");
|
ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_040_007_000, "作文不存在!");
|
||||||
ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_022_07_001, "写作生成异常!");
|
ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_040_07_001, "写作生成异常!");
|
||||||
|
|
||||||
// ========== API 思维导图 1-040-008-000 ==========
|
// ========== API 思维导图 1-040-008-000 ==========
|
||||||
ErrorCode MIND_MAP_NOT_EXISTS = new ErrorCode(1_040_008_000, "思维导图不存在!");
|
ErrorCode MIND_MAP_NOT_EXISTS = new ErrorCode(1_040_008_000, "思维导图不存在!");
|
||||||
|
|
||||||
// ========== API 知识库 1-022-008-000 ==========
|
// ========== API 知识库 1-040-009-000 ==========
|
||||||
ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_022_008_000, "知识库不存在!");
|
ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_040_009_000, "知识库不存在!");
|
||||||
ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_022_008_001, "文档不存在!");
|
|
||||||
ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_022_008_002, "段落不存在!");
|
ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_040_009_101, "文档不存在!");
|
||||||
|
ErrorCode KNOWLEDGE_DOCUMENT_FILE_EMPTY = new ErrorCode(1_040_009_102, "文档内容为空!");
|
||||||
|
ErrorCode KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL = new ErrorCode(1_040_009_102, "文件下载失败!");
|
||||||
|
ErrorCode KNOWLEDGE_DOCUMENT_FILE_READ_FAIL = new ErrorCode(1_040_009_102, "文档加载失败!");
|
||||||
|
|
||||||
|
ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_040_009_202, "段落不存在!");
|
||||||
|
ErrorCode KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG = new ErrorCode(1_040_009_203, "内容 Token 数为 {},超过最大限制 {}");
|
||||||
|
|
||||||
|
// ========== AI 工具 1-040-010-000 ==========
|
||||||
|
ErrorCode TOOL_NOT_EXISTS = new ErrorCode(1_040_010_000, "工具不存在");
|
||||||
|
ErrorCode TOOL_NAME_NOT_EXISTS = new ErrorCode(1_040_010_001, "工具({})找不到 Bean");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.ai.enums.knowledge;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AI 知识库-文档状态的枚举
|
|
||||||
*
|
|
||||||
* @author xiaoxin
|
|
||||||
*/
|
|
||||||
@AllArgsConstructor
|
|
||||||
@Getter
|
|
||||||
public enum AiKnowledgeDocumentStatusEnum implements ArrayValuable<Integer> {
|
|
||||||
|
|
||||||
IN_PROGRESS(10, "索引中"),
|
|
||||||
SUCCESS(20, "可用"),
|
|
||||||
FAIL(30, "失败");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 状态
|
|
||||||
*/
|
|
||||||
private final Integer status;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 状态名
|
|
||||||
*/
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiKnowledgeDocumentStatusEnum::getStatus).toArray(Integer[]::new);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Integer[] array() {
|
|
||||||
return ARRAYS;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -12,15 +12,18 @@ import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessage
|
|||||||
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
||||||
import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService;
|
import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService;
|
||||||
import cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService;
|
import cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
|
import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.annotation.security.PermitAll;
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@ -33,7 +36,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||||
|
|
||||||
@Tag(name = "管理后台 - 聊天消息")
|
@Tag(name = "管理后台 - 聊天消息")
|
||||||
@ -48,6 +51,10 @@ public class AiChatMessageController {
|
|||||||
private AiChatConversationService chatConversationService;
|
private AiChatConversationService chatConversationService;
|
||||||
@Resource
|
@Resource
|
||||||
private AiChatRoleService chatRoleService;
|
private AiChatRoleService chatRoleService;
|
||||||
|
@Resource
|
||||||
|
private AiKnowledgeSegmentService knowledgeSegmentService;
|
||||||
|
@Resource
|
||||||
|
private AiKnowledgeDocumentService knowledgeDocumentService;
|
||||||
|
|
||||||
@Operation(summary = "发送消息(段式)", description = "一次性返回,响应较慢")
|
@Operation(summary = "发送消息(段式)", description = "一次性返回,响应较慢")
|
||||||
@PostMapping("/send")
|
@PostMapping("/send")
|
||||||
@ -57,7 +64,6 @@ public class AiChatMessageController {
|
|||||||
|
|
||||||
@Operation(summary = "发送消息(流式)", description = "流式返回,响应较快")
|
@Operation(summary = "发送消息(流式)", description = "流式返回,响应较快")
|
||||||
@PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
@PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||||
@PermitAll // 解决 SSE 最终响应的时候,会被 Access Denied 拦截的问题
|
|
||||||
public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) {
|
public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) {
|
||||||
return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId());
|
return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId());
|
||||||
}
|
}
|
||||||
@ -71,8 +77,38 @@ public class AiChatMessageController {
|
|||||||
if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {
|
if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {
|
||||||
return success(Collections.emptyList());
|
return success(Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
// 1. 获取消息列表
|
||||||
List<AiChatMessageDO> messageList = chatMessageService.getChatMessageListByConversationId(conversationId);
|
List<AiChatMessageDO> messageList = chatMessageService.getChatMessageListByConversationId(conversationId);
|
||||||
return success(BeanUtils.toBean(messageList, AiChatMessageRespVO.class));
|
if (CollUtil.isEmpty(messageList)) {
|
||||||
|
return success(Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 拼接数据,主要是知识库段落信息
|
||||||
|
Map<Long, AiKnowledgeSegmentDO> segmentMap = knowledgeSegmentService.getKnowledgeSegmentMap(convertListByFlatMap(messageList,
|
||||||
|
message -> CollUtil.isEmpty(message.getSegmentIds()) ? null : message.getSegmentIds().stream()));
|
||||||
|
Map<Long, AiKnowledgeDocumentDO> documentMap = knowledgeDocumentService.getKnowledgeDocumentMap(
|
||||||
|
convertList(segmentMap.values(), AiKnowledgeSegmentDO::getDocumentId));
|
||||||
|
List<AiChatMessageRespVO> messageVOList = BeanUtils.toBean(messageList, AiChatMessageRespVO.class);
|
||||||
|
for (int i = 0; i < messageList.size(); i++) {
|
||||||
|
AiChatMessageDO message = messageList.get(i);
|
||||||
|
if (CollUtil.isEmpty(message.getSegmentIds())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 设置知识库段落信息
|
||||||
|
messageVOList.get(i).setSegments(convertList(message.getSegmentIds(), segmentId -> {
|
||||||
|
AiKnowledgeSegmentDO segment = segmentMap.get(segmentId);
|
||||||
|
if (segment == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());
|
||||||
|
if (document == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new AiChatMessageRespVO.KnowledgeSegment().setId(segment.getId()).setContent(segment.getContent())
|
||||||
|
.setDocumentId(segment.getDocumentId()).setDocumentName(document.getName());
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return success(messageVOList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "删除消息")
|
@Operation(summary = "删除消息")
|
||||||
@ -105,7 +141,8 @@ public class AiChatMessageController {
|
|||||||
Map<Long, AiChatRoleDO> roleMap = chatRoleService.getChatRoleMap(
|
Map<Long, AiChatRoleDO> roleMap = chatRoleService.getChatRoleMap(
|
||||||
convertSet(pageResult.getList(), AiChatMessageDO::getRoleId));
|
convertSet(pageResult.getList(), AiChatMessageDO::getRoleId));
|
||||||
return success(BeanUtils.toBean(pageResult, AiChatMessageRespVO.class,
|
return success(BeanUtils.toBean(pageResult, AiChatMessageRespVO.class,
|
||||||
respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(), role -> respVO.setRoleName(role.getName()))));
|
respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(),
|
||||||
|
role -> respVO.setRoleName(role.getName()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "管理员删除消息")
|
@Operation(summary = "管理员删除消息")
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;
|
package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
||||||
import com.fhs.core.trans.anno.Trans;
|
import com.fhs.core.trans.anno.Trans;
|
||||||
import com.fhs.core.trans.constant.TransType;
|
import com.fhs.core.trans.constant.TransType;
|
||||||
@ -31,7 +31,7 @@ public class AiChatConversationRespVO implements VO {
|
|||||||
private Long roleId;
|
private Long roleId;
|
||||||
|
|
||||||
@Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
@Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = "name", ref = "modelName")
|
@Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = "name", ref = "modelName")
|
||||||
private Long modelId;
|
private Long modelId;
|
||||||
|
|
||||||
@Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922")
|
@Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922")
|
||||||
|
@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI 聊天消息 Response VO")
|
@Schema(description = "管理后台 - AI 聊天消息 Response VO")
|
||||||
@Data
|
@Data
|
||||||
@ -39,6 +40,12 @@ public class AiChatMessageRespVO {
|
|||||||
@Schema(description = "是否携带上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
@Schema(description = "是否携带上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||||
private Boolean useContext;
|
private Boolean useContext;
|
||||||
|
|
||||||
|
@Schema(description = "知识库段落编号数组", example = "[1,2,3]")
|
||||||
|
private List<Long> segmentIds;
|
||||||
|
|
||||||
|
@Schema(description = "知识库段落数组")
|
||||||
|
private List<KnowledgeSegment> segments;
|
||||||
|
|
||||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51")
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51")
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
@ -47,4 +54,22 @@ public class AiChatMessageRespVO {
|
|||||||
@Schema(description = "角色名字", example = "小黄")
|
@Schema(description = "角色名字", example = "小黄")
|
||||||
private String roleName;
|
private String roleName;
|
||||||
|
|
||||||
|
@Schema(description = "知识库段落", example = "Java 开发手册")
|
||||||
|
@Data
|
||||||
|
public static class KnowledgeSegment {
|
||||||
|
|
||||||
|
@Schema(description = "段落编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
|
||||||
|
private Long documentId;
|
||||||
|
|
||||||
|
@Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品使用手册")
|
||||||
|
private String documentName;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI 聊天消息发送 Response VO")
|
@Schema(description = "管理后台 - AI 聊天消息发送 Response VO")
|
||||||
@Data
|
@Data
|
||||||
@ -28,6 +29,12 @@ public class AiChatMessageSendRespVO {
|
|||||||
@Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊")
|
@Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊")
|
||||||
private String content;
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "知识库段落编号数组", example = "[1,2,3]")
|
||||||
|
private List<Long> segmentIds;
|
||||||
|
|
||||||
|
@Schema(description = "知识库段落数组")
|
||||||
|
private List<AiChatMessageRespVO.KnowledgeSegment> segments;
|
||||||
|
|
||||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@ -14,18 +14,15 @@ import java.util.Map;
|
|||||||
@Data
|
@Data
|
||||||
public class AiImageDrawReqVO {
|
public class AiImageDrawReqVO {
|
||||||
|
|
||||||
@Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
|
@Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||||
private String platform; // 参见 AiPlatformEnum 枚举
|
@NotNull(message = "模型编号不能为空")
|
||||||
|
private Long modelId;
|
||||||
|
|
||||||
@Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "画一个长城")
|
@Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "画一个长城")
|
||||||
@NotEmpty(message = "提示词不能为空")
|
@NotEmpty(message = "提示词不能为空")
|
||||||
@Size(max = 1200, message = "提示词最大 1200")
|
@Size(max = 1200, message = "提示词最大 1200")
|
||||||
private String prompt;
|
private String prompt;
|
||||||
|
|
||||||
@Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "stable-diffusion-v1-6")
|
|
||||||
@NotEmpty(message = "模型不能为空")
|
|
||||||
private String model;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1. dall-e-2 模型:256x256、512x512、1024x1024
|
* 1. dall-e-2 模型:256x256、512x512、1024x1024
|
||||||
* 2. dall-e-3 模型:1024x1024, 1792x1024, 或 1024x1792
|
* 2. dall-e-3 模型:1024x1024, 1792x1024, 或 1024x1792
|
||||||
|
@ -13,9 +13,9 @@ public class AiMidjourneyImagineReqVO {
|
|||||||
@NotEmpty(message = "提示词不能为空!")
|
@NotEmpty(message = "提示词不能为空!")
|
||||||
private String prompt;
|
private String prompt;
|
||||||
|
|
||||||
@Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "midjourney")
|
@Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
@NotEmpty(message = "模型不能为空")
|
@NotNull(message = "模型编号不能为空")
|
||||||
private String model; // 参考 MidjourneyApi.ModelEnum
|
private Long modelId;
|
||||||
|
|
||||||
@Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
@NotNull(message = "图片宽度不能为空")
|
@NotNull(message = "图片宽度不能为空")
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
### 创建知识库
|
||||||
|
POST {{baseUrl}}/ai/knowledge/create
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: {{token}}
|
||||||
|
tenant-id: {{adminTenantId}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "测试标题",
|
||||||
|
"description": "测试描述",
|
||||||
|
"embeddingModelId": 30,
|
||||||
|
"topK": 3,
|
||||||
|
"similarityThreshold": 0.5,
|
||||||
|
"status": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
### 更新知识库
|
||||||
|
PUT {{baseUrl}}/ai/knowledge/update
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: {{token}}
|
||||||
|
tenant-id: {{adminTenantId}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "测试标题(更新)",
|
||||||
|
"description": "测试描述",
|
||||||
|
"embeddingModelId": 30,
|
||||||
|
"topK": 5,
|
||||||
|
"similarityThreshold": 0.6,
|
||||||
|
"status": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
### 获取知识库分页
|
||||||
|
GET {{baseUrl}}/ai/knowledge/page?pageNo=1&pageSize=10
|
||||||
|
Authorization: {{token}}
|
||||||
|
tenant-id: {{adminTenantId}}
|
@ -1,23 +1,27 @@
|
|||||||
package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
|
package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
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.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeRespVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeRespVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
|
||||||
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
|
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
|
|
||||||
@Tag(name = "管理后台 - AI 知识库")
|
@Tag(name = "管理后台 - AI 知识库")
|
||||||
@RestController
|
@RestController
|
||||||
@ -30,21 +34,42 @@ public class AiKnowledgeController {
|
|||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@Operation(summary = "获取知识库分页")
|
@Operation(summary = "获取知识库分页")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
|
||||||
public CommonResult<PageResult<AiKnowledgeRespVO>> getKnowledgePage(@Valid AiKnowledgePageReqVO pageReqVO) {
|
public CommonResult<PageResult<AiKnowledgeRespVO>> getKnowledgePage(@Valid AiKnowledgePageReqVO pageReqVO) {
|
||||||
PageResult<AiKnowledgeDO> pageResult = knowledgeService.getKnowledgePage(getLoginUserId(), pageReqVO);
|
PageResult<AiKnowledgeDO> pageResult = knowledgeService.getKnowledgePage(pageReqVO);
|
||||||
return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class));
|
return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获得知识库")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
|
||||||
|
public CommonResult<AiKnowledgeRespVO> getKnowledge(@RequestParam("id") Long id) {
|
||||||
|
AiKnowledgeDO knowledge = knowledgeService.getKnowledge(id);
|
||||||
|
return success(BeanUtils.toBean(knowledge, AiKnowledgeRespVO.class));
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
@Operation(summary = "创建知识库")
|
@Operation(summary = "创建知识库")
|
||||||
public CommonResult<Long> createKnowledge(@RequestBody @Valid AiKnowledgeCreateReqVO createReqVO) {
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
|
||||||
return success(knowledgeService.createKnowledge(createReqVO, getLoginUserId()));
|
public CommonResult<Long> createKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO createReqVO) {
|
||||||
|
return success(knowledgeService.createKnowledge(createReqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/update")
|
@PutMapping("/update")
|
||||||
@Operation(summary = "更新知识库")
|
@Operation(summary = "更新知识库")
|
||||||
public CommonResult<Boolean> updateKnowledge(@RequestBody @Valid AiKnowledgeUpdateReqVO updateReqVO) {
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
|
||||||
knowledgeService.updateKnowledge(updateReqVO, getLoginUserId());
|
public CommonResult<Boolean> updateKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO updateReqVO) {
|
||||||
|
knowledgeService.updateKnowledge(updateReqVO);
|
||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/simple-list")
|
||||||
|
@Operation(summary = "获得知识库的精简列表")
|
||||||
|
public CommonResult<List<AiKnowledgeRespVO>> getKnowledgeSimpleList() {
|
||||||
|
List<AiKnowledgeDO> list = knowledgeService.getKnowledgeSimpleListByStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||||
|
return success(convertList(list, knowledge -> new AiKnowledgeRespVO()
|
||||||
|
.setId(knowledge.getId()).setName(knowledge.getName())));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
### 创建知识文档
|
||||||
|
POST {{baseUrl}}/ai/knowledge/document/create
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
|
tenant-id: {{adminTenantId}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"knowledgeId": 2,
|
||||||
|
"name": "测试文档",
|
||||||
|
"url": "https://static.iocoder.cn/README.md",
|
||||||
|
"segmentMaxTokens": 800
|
||||||
|
}
|
||||||
|
|
||||||
|
### 批量创建知识文档
|
||||||
|
POST {{baseUrl}}/ai/knowledge/document/create-list
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
|
tenant-id: {{adminTenantId}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"knowledgeId": 1,
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"name": "测试文档1",
|
||||||
|
"url": "https://static.iocoder.cn/README.md",
|
||||||
|
"segmentMaxTokens": 800
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "测试文档2",
|
||||||
|
"url": "https://static.iocoder.cn/README_yudao.md",
|
||||||
|
"segmentMaxTokens": 400
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
|
|||||||
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.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.*;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentRespVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
|
||||||
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
|
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
|
||||||
@ -13,9 +11,12 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
|
||||||
@Tag(name = "管理后台 - AI 知识库文档")
|
@Tag(name = "管理后台 - AI 知识库文档")
|
||||||
@ -27,25 +28,63 @@ public class AiKnowledgeDocumentController {
|
|||||||
@Resource
|
@Resource
|
||||||
private AiKnowledgeDocumentService documentService;
|
private AiKnowledgeDocumentService documentService;
|
||||||
|
|
||||||
@PostMapping("/create")
|
|
||||||
@Operation(summary = "新建文档")
|
|
||||||
public CommonResult<Long> createKnowledgeDocument(@Valid AiKnowledgeDocumentCreateReqVO reqVO) {
|
|
||||||
Long knowledgeDocumentId = documentService.createKnowledgeDocument(reqVO);
|
|
||||||
return success(knowledgeDocumentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@Operation(summary = "获取文档分页")
|
@Operation(summary = "获取文档分页")
|
||||||
public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPage(@Valid AiKnowledgeDocumentPageReqVO pageReqVO) {
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
|
||||||
|
public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPage(
|
||||||
|
@Valid AiKnowledgeDocumentPageReqVO pageReqVO) {
|
||||||
PageResult<AiKnowledgeDocumentDO> pageResult = documentService.getKnowledgeDocumentPage(pageReqVO);
|
PageResult<AiKnowledgeDocumentDO> pageResult = documentService.getKnowledgeDocumentPage(pageReqVO);
|
||||||
return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class));
|
return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获取文档详情")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
|
||||||
|
public CommonResult<AiKnowledgeDocumentRespVO> getKnowledgeDocument(@RequestParam("id") Long id) {
|
||||||
|
AiKnowledgeDocumentDO document = documentService.getKnowledgeDocument(id);
|
||||||
|
return success(BeanUtils.toBean(document, AiKnowledgeDocumentRespVO.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/create")
|
||||||
|
@Operation(summary = "新建文档(单个)")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
|
||||||
|
public CommonResult<Long> createKnowledgeDocument(@RequestBody @Valid AiKnowledgeDocumentCreateReqVO reqVO) {
|
||||||
|
Long id = documentService.createKnowledgeDocument(reqVO);
|
||||||
|
return success(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/create-list")
|
||||||
|
@Operation(summary = "新建文档(多个)")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
|
||||||
|
public CommonResult<List<Long>> createKnowledgeDocumentList(
|
||||||
|
@RequestBody @Valid AiKnowledgeDocumentCreateListReqVO reqVO) {
|
||||||
|
List<Long> ids = documentService.createKnowledgeDocumentList(reqVO);
|
||||||
|
return success(ids);
|
||||||
|
}
|
||||||
|
|
||||||
@PutMapping("/update")
|
@PutMapping("/update")
|
||||||
@Operation(summary = "更新文档")
|
@Operation(summary = "更新文档")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
|
||||||
public CommonResult<Boolean> updateKnowledgeDocument(@Valid @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) {
|
public CommonResult<Boolean> updateKnowledgeDocument(@Valid @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) {
|
||||||
documentService.updateKnowledgeDocument(reqVO);
|
documentService.updateKnowledgeDocument(reqVO);
|
||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update-status")
|
||||||
|
@Operation(summary = "更新文档状态")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
|
||||||
|
public CommonResult<Boolean> updateKnowledgeDocumentStatus(
|
||||||
|
@Valid @RequestBody AiKnowledgeDocumentUpdateStatusReqVO reqVO) {
|
||||||
|
documentService.updateKnowledgeDocumentStatus(reqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete")
|
||||||
|
@Operation(summary = "删除文档")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:delete')")
|
||||||
|
public CommonResult<Boolean> deleteKnowledgeDocument(@RequestParam("id") Long id) {
|
||||||
|
documentService.deleteKnowledgeDocument(id);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
### 切片内容
|
||||||
|
GET {{baseUrl}}/ai/knowledge/segment/split?url=https://static.iocoder.cn/README_yudao.md&segmentMaxTokens=800
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
|
tenant-id: {{adminTenantId}}
|
||||||
|
|
||||||
|
### 搜索段落内容
|
||||||
|
GET {{baseUrl}}/ai/knowledge/segment/search?knowledgeId=2&content=如何使用这个产品&topK=5&similarityThreshold=0.1
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
|
tenant-id: {{adminTenantId}}
|
||||||
|
|
||||||
|
### 获取文档处理列表
|
||||||
|
GET {{baseUrl}}/ai/knowledge/segment/get-process-list?documentIds=1,2,3
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
|
tenant-id: {{adminTenantId}}
|
@ -1,22 +1,34 @@
|
|||||||
package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
|
package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
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.collection.MapUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.*;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentRespVO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
|
||||||
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
|
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameters;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
import org.hibernate.validator.constraints.URL;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||||
|
|
||||||
@Tag(name = "管理后台 - AI 知识库段落")
|
@Tag(name = "管理后台 - AI 知识库段落")
|
||||||
@RestController
|
@RestController
|
||||||
@ -26,26 +38,93 @@ public class AiKnowledgeSegmentController {
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiKnowledgeSegmentService segmentService;
|
private AiKnowledgeSegmentService segmentService;
|
||||||
|
@Resource
|
||||||
|
private AiKnowledgeDocumentService documentService;
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获取段落详情")
|
||||||
|
@Parameter(name = "id", description = "段落编号", required = true, example = "1024")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
|
||||||
|
public CommonResult<AiKnowledgeSegmentRespVO> getKnowledgeSegment(@RequestParam("id") Long id) {
|
||||||
|
AiKnowledgeSegmentDO segment = segmentService.getKnowledgeSegment(id);
|
||||||
|
return success(BeanUtils.toBean(segment, AiKnowledgeSegmentRespVO.class));
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
@Operation(summary = "获取段落分页")
|
@Operation(summary = "获取段落分页")
|
||||||
public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPage(@Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
|
||||||
|
public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPage(
|
||||||
|
@Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
|
||||||
PageResult<AiKnowledgeSegmentDO> pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO);
|
PageResult<AiKnowledgeSegmentDO> pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO);
|
||||||
return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class));
|
return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/create")
|
||||||
|
@Operation(summary = "创建段落")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
|
||||||
|
public CommonResult<Long> createKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO createReqVO) {
|
||||||
|
return success(segmentService.createKnowledgeSegment(createReqVO));
|
||||||
|
}
|
||||||
|
|
||||||
@PutMapping("/update")
|
@PutMapping("/update")
|
||||||
@Operation(summary = "更新段落内容")
|
@Operation(summary = "更新段落内容")
|
||||||
public CommonResult<Boolean> updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentUpdateReqVO reqVO) {
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
|
||||||
|
public CommonResult<Boolean> updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO reqVO) {
|
||||||
segmentService.updateKnowledgeSegment(reqVO);
|
segmentService.updateKnowledgeSegment(reqVO);
|
||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/update-status")
|
@PutMapping("/update-status")
|
||||||
@Operation(summary = "启禁用段落内容")
|
@Operation(summary = "启禁用段落内容")
|
||||||
public CommonResult<Boolean> updateKnowledgeSegmentStatus(@Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
|
||||||
|
public CommonResult<Boolean> updateKnowledgeSegmentStatus(
|
||||||
|
@Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
|
||||||
segmentService.updateKnowledgeSegmentStatus(reqVO);
|
segmentService.updateKnowledgeSegmentStatus(reqVO);
|
||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/split")
|
||||||
|
@Operation(summary = "切片内容")
|
||||||
|
@Parameters({
|
||||||
|
@Parameter(name = "url", description = "文档 URL", required = true),
|
||||||
|
@Parameter(name = "segmentMaxTokens", description = "分段的最大 Token 数", required = true)
|
||||||
|
})
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
|
||||||
|
public CommonResult<List<AiKnowledgeSegmentRespVO>> splitContent(
|
||||||
|
@RequestParam("url") @URL String url,
|
||||||
|
@RequestParam(value = "segmentMaxTokens") Integer segmentMaxTokens) {
|
||||||
|
List<AiKnowledgeSegmentDO> segments = segmentService.splitContent(url, segmentMaxTokens);
|
||||||
|
return success(BeanUtils.toBean(segments, AiKnowledgeSegmentRespVO.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get-process-list")
|
||||||
|
@Operation(summary = "获取文档处理列表")
|
||||||
|
@Parameter(name = "documentIds", description = "文档编号列表", required = true, example = "1,2,3")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
|
||||||
|
public CommonResult<List<AiKnowledgeSegmentProcessRespVO>> getKnowledgeSegmentProcessList(
|
||||||
|
@RequestParam("documentIds") List<Long> documentIds) {
|
||||||
|
List<AiKnowledgeSegmentProcessRespVO> list = segmentService.getKnowledgeSegmentProcessList(documentIds);
|
||||||
|
return success(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/search")
|
||||||
|
@Operation(summary = "搜索段落内容")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
|
||||||
|
public CommonResult<List<AiKnowledgeSegmentSearchRespVO>> searchKnowledgeSegment(
|
||||||
|
@Valid AiKnowledgeSegmentSearchReqVO reqVO) {
|
||||||
|
// 1. 搜索段落
|
||||||
|
List<AiKnowledgeSegmentSearchRespBO> segments = segmentService
|
||||||
|
.searchKnowledgeSegment(BeanUtils.toBean(reqVO, AiKnowledgeSegmentSearchReqBO.class));
|
||||||
|
if (CollUtil.isEmpty(segments)) {
|
||||||
|
return success(Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 拼接 VO
|
||||||
|
Map<Long, AiKnowledgeDocumentDO> documentMap = documentService.getKnowledgeDocumentMap(convertSet(
|
||||||
|
segments, AiKnowledgeSegmentSearchRespBO::getDocumentId));
|
||||||
|
return success(BeanUtils.toBean(segments, AiKnowledgeSegmentSearchRespVO.class,
|
||||||
|
segment -> MapUtils.findAndThen(documentMap, segment.getDocumentId(),
|
||||||
|
document -> segment.setDocumentName(document.getName()))));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.hibernate.validator.constraints.URL;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - AI 知识库文档批量创建 Request VO")
|
||||||
|
@Data
|
||||||
|
public class AiKnowledgeDocumentCreateListReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204")
|
||||||
|
@NotNull(message = "知识库编号不能为空")
|
||||||
|
private Long knowledgeId;
|
||||||
|
|
||||||
|
@Schema(description = "分段的最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
|
||||||
|
@NotNull(message = "分段的最大 Token 数不能为空")
|
||||||
|
private Integer segmentMaxTokens;
|
||||||
|
|
||||||
|
@Schema(description = "文档列表", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
@NotEmpty(message = "文档列表不能为空")
|
||||||
|
private List<Document> list;
|
||||||
|
|
||||||
|
@Schema(description = "文档")
|
||||||
|
@Data
|
||||||
|
public static class Document {
|
||||||
|
|
||||||
|
@Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "三方登陆")
|
||||||
|
@NotBlank(message = "文档名称不能为空")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
|
||||||
|
@URL(message = "文档 URL 格式不正确")
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -8,6 +8,9 @@ import lombok.Data;
|
|||||||
@Data
|
@Data
|
||||||
public class AiKnowledgeDocumentPageReqVO extends PageParam {
|
public class AiKnowledgeDocumentPageReqVO extends PageParam {
|
||||||
|
|
||||||
|
@Schema(description = "知识库编号", example = "1")
|
||||||
|
private Long knowledgeId;
|
||||||
|
|
||||||
@Schema(description = "文档名称", example = "Java 开发手册")
|
@Schema(description = "文档名称", example = "Java 开发手册")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@ -1,38 +1,45 @@
|
|||||||
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
|
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI 知识库-文档 Response VO")
|
import java.time.LocalDateTime;
|
||||||
@Data
|
|
||||||
public class AiKnowledgeDocumentRespVO extends PageParam {
|
|
||||||
|
|
||||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
|
@Schema(description = "管理后台 - AI 知识库文档 Response VO")
|
||||||
|
@Data
|
||||||
|
public class AiKnowledgeDocumentRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
|
@Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
|
||||||
private Long knowledgeId;
|
private Long knowledgeId;
|
||||||
|
|
||||||
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
|
@Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 是一门面向对象的语言.....")
|
@Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
|
||||||
private String content;
|
|
||||||
|
|
||||||
@Schema(description = "文档 url", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
|
|
||||||
private String url;
|
private String url;
|
||||||
|
|
||||||
@Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
@Schema(description = "文档内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 是一门面向对象的语言.....")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "文档内容长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||||
|
private Integer contentLength;
|
||||||
|
|
||||||
|
@Schema(description = "文档 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||||
private Integer tokens;
|
private Integer tokens;
|
||||||
|
|
||||||
@Schema(description = "字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008")
|
@Schema(description = "分片最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")
|
||||||
private Integer wordCount;
|
private Integer segmentMaxTokens;
|
||||||
|
|
||||||
@Schema(description = "切片状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "召回次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||||
private Integer sliceStatus;
|
private Integer retrievalCount;
|
||||||
|
|
||||||
@Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,21 @@
|
|||||||
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
|
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
|
||||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - AI 知识库文档更新 Request VO")
|
||||||
@Schema(description = "管理后台 - AI 更新 知识库-文档 Request VO")
|
|
||||||
@Data
|
@Data
|
||||||
public class AiKnowledgeDocumentUpdateReqVO {
|
public class AiKnowledgeDocumentUpdateReqVO {
|
||||||
|
|
||||||
|
|
||||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
|
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
|
||||||
@NotNull(message = "编号不能为空")
|
@NotNull(message = "编号不能为空")
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@Schema(description = "是否启用", example = "1")
|
|
||||||
@InEnum(CommonStatusEnum.class)
|
|
||||||
private Integer status;
|
|
||||||
|
|
||||||
@Schema(description = "名称", example = "Java 开发手册")
|
@Schema(description = "名称", example = "Java 开发手册")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "分片最大 Token 数", example = "1000")
|
||||||
|
private Integer segmentMaxTokens;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
|
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - AI 知识库文档更新状态 Request VO")
|
||||||
|
@Data
|
||||||
|
public class AiKnowledgeDocumentUpdateStatusReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
|
||||||
|
@NotNull(message = "编号不能为空")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||||
|
@NotNull(message = "状态不能为空")
|
||||||
|
@InEnum(CommonStatusEnum.class)
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
}
|
@ -23,24 +23,8 @@ public class AiKnowledgeDocumentCreateReqVO {
|
|||||||
@URL(message = "文档 URL 格式不正确")
|
@URL(message = "文档 URL 格式不正确")
|
||||||
private String url;
|
private String url;
|
||||||
|
|
||||||
@Schema(description = "每个段落的目标 token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
|
@Schema(description = "分段的最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
|
||||||
@NotNull(message = "每个段落的目标 token 数不能为空")
|
@NotNull(message = "分段的最大 Token 数不能为空")
|
||||||
private Integer defaultSegmentTokens;
|
private Integer segmentMaxTokens;
|
||||||
|
|
||||||
@Schema(description = "每个段落的最小字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "350")
|
|
||||||
@NotNull(message = "每个段落的最小字符数不能为空")
|
|
||||||
private Integer minSegmentWordCount;
|
|
||||||
|
|
||||||
@Schema(description = "丢弃阈值:低于此阈值的段落会被丢弃", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
|
||||||
@NotNull(message = "丢弃阈值不能为空")
|
|
||||||
private Integer minChunkLengthToEmbed;
|
|
||||||
|
|
||||||
@Schema(description = "最大段落数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
|
|
||||||
@NotNull(message = "最大段落数不能为空")
|
|
||||||
private Integer maxNumSegments;
|
|
||||||
|
|
||||||
@Schema(description = "分块是否保留分隔符", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
|
||||||
@NotNull(message = "分块是否保留分隔符不能为空")
|
|
||||||
private Boolean keepSeparator;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,29 @@
|
|||||||
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
|
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||||
|
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI 知识库的分页 Request VO")
|
@Schema(description = "管理后台 - AI 知识库的分页 Request VO")
|
||||||
@Data
|
@Data
|
||||||
public class AiKnowledgePageReqVO extends PageParam {
|
public class AiKnowledgePageReqVO extends PageParam {
|
||||||
|
|
||||||
@Schema(description = "知识库名称", example = "Java 开发手册")
|
@Schema(description = "知识库名称", example = "芋艿")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "是否启用", example = "1")
|
||||||
|
@InEnum(CommonStatusEnum.class)
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] createTime;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
|
|||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI 知识库 Response VO")
|
@Schema(description = "管理后台 - AI 知识库 Response VO")
|
||||||
@Data
|
@Data
|
||||||
@ -17,10 +18,22 @@ public class AiKnowledgeRespVO {
|
|||||||
@Schema(description = "知识库描述", example = "帮助你快速构建系统")
|
@Schema(description = "知识库描述", example = "帮助你快速构建系统")
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
@Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14")
|
@Schema(description = "向量模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14")
|
||||||
private Long modelId;
|
private Long embeddingModelId;
|
||||||
|
|
||||||
@Schema(description = "模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen-72b-chat")
|
@Schema(description = "向量模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen-72b-chat")
|
||||||
private String model;
|
private String embeddingModel;
|
||||||
|
|
||||||
|
@Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
|
||||||
|
private Integer topK;
|
||||||
|
|
||||||
|
@Schema(description = "相似度阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.7")
|
||||||
|
private Double similarityThreshold;
|
||||||
|
|
||||||
|
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
|
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
|
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.util.List;
|
@Schema(description = "管理后台 - AI 知识库新增/修改 Request VO")
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI 知识库创建 Request VO")
|
|
||||||
@Data
|
@Data
|
||||||
public class AiKnowledgeCreateReqVO {
|
public class AiKnowledgeSaveReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "对话编号", example = "1204")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
@Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南")
|
@Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南")
|
||||||
@NotBlank(message = "知识库名称不能为空")
|
@NotBlank(message = "知识库名称不能为空")
|
||||||
@ -18,19 +21,21 @@ public class AiKnowledgeCreateReqVO {
|
|||||||
@Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "存储 ruoyi-vue-pro 操作文档")
|
@Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "存储 ruoyi-vue-pro 操作文档")
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
@Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3]")
|
@Schema(description = "向量模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
private List<Long> visibilityPermissions;
|
@NotNull(message = "向量模型不能为空")
|
||||||
|
private Long embeddingModelId;
|
||||||
@Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
|
||||||
@NotNull(message = "嵌入模型不能为空")
|
|
||||||
private Long modelId;
|
|
||||||
|
|
||||||
@Schema(description = "相似性阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.5")
|
|
||||||
@NotNull(message = "相似性阈值不能为空")
|
|
||||||
private Double similarityThreshold;
|
|
||||||
|
|
||||||
@Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
|
@Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
|
||||||
@NotNull(message = "topK 不能为空")
|
@NotNull(message = "topK 不能为空")
|
||||||
private Integer topK;
|
private Integer topK;
|
||||||
|
|
||||||
|
@Schema(description = "相似性阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.5")
|
||||||
|
@NotNull(message = "相似性阈值不能为空")
|
||||||
|
private Double similarityThreshold;
|
||||||
|
|
||||||
|
@Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
@NotNull(message = "是否启用不能为空")
|
||||||
|
@InEnum(CommonStatusEnum.class)
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
}
|
}
|
@ -1,32 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI 知识库更新【我的】 Request VO")
|
|
||||||
@Data
|
|
||||||
public class AiKnowledgeUpdateReqVO {
|
|
||||||
|
|
||||||
@Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204")
|
|
||||||
@NotNull(message = "知识库编号不能为空")
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "")
|
|
||||||
@NotBlank(message = "知识库名称不能为空")
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "")
|
|
||||||
private String description;
|
|
||||||
|
|
||||||
@Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
|
|
||||||
private List<Long> visibilityPermissions;
|
|
||||||
|
|
||||||
@Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
|
||||||
@NotNull(message = "嵌入模型不能为空")
|
|
||||||
private Long modelId;
|
|
||||||
|
|
||||||
}
|
|
@ -1,6 +1,8 @@
|
|||||||
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
|
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||||
|
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@ -8,13 +10,14 @@ import lombok.Data;
|
|||||||
@Data
|
@Data
|
||||||
public class AiKnowledgeSegmentPageReqVO extends PageParam {
|
public class AiKnowledgeSegmentPageReqVO extends PageParam {
|
||||||
|
|
||||||
@Schema(description = "分段状态", example = "1")
|
|
||||||
private Integer status;
|
|
||||||
|
|
||||||
@Schema(description = "文档编号", example = "1")
|
@Schema(description = "文档编号", example = "1")
|
||||||
private Integer documentId;
|
private Integer documentId;
|
||||||
|
|
||||||
@Schema(description = "分段内容关键字", example = "Java 开发")
|
@Schema(description = "分段内容关键字", example = "Java 开发")
|
||||||
private String keyword;
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "分段状态", example = "1")
|
||||||
|
@InEnum(CommonStatusEnum.class)
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - AI 知识库段落向量进度 Response VO")
|
||||||
|
@Data
|
||||||
|
public class AiKnowledgeSegmentProcessRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
private Long documentId;
|
||||||
|
|
||||||
|
@Schema(description = "总段落数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||||
|
private Long count;
|
||||||
|
|
||||||
|
@Schema(description = "已向量化段落数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
|
||||||
|
private Long embeddingCount;
|
||||||
|
|
||||||
|
}
|
@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
|
|||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI 知识库-文档 Response VO")
|
@Schema(description = "管理后台 - AI 知识库文档分片 Response VO")
|
||||||
@Data
|
@Data
|
||||||
public class AiKnowledgeSegmentRespVO {
|
public class AiKnowledgeSegmentRespVO {
|
||||||
|
|
||||||
@ -22,13 +22,19 @@ public class AiKnowledgeSegmentRespVO {
|
|||||||
@Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
|
@Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
|
||||||
private String content;
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "切片内容长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||||
|
private Integer contentLength;
|
||||||
|
|
||||||
@Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
@Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||||
private Integer tokens;
|
private Integer tokens;
|
||||||
|
|
||||||
@Schema(description = "字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008")
|
@Schema(description = "召回次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||||
private Integer wordCount;
|
private Integer retrievalCount;
|
||||||
|
|
||||||
@Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private Long createTime;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - AI 新增/修改知识库段落 request VO")
|
||||||
|
@Data
|
||||||
|
public class AiKnowledgeSegmentSaveReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "编号", example = "24790")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "知识库文档编号", example = "1024")
|
||||||
|
private Long documentId;
|
||||||
|
|
||||||
|
@Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
|
||||||
|
@NotEmpty(message = "切片内容不能为空")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
}
|
@ -3,15 +3,25 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
|
|||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI 知识库段落召回 Request VO")
|
@Schema(description = "管理后台 - AI 知识库段落搜索 Request VO")
|
||||||
@Data
|
@Data
|
||||||
public class AiKnowledgeSegmentSearchReqVO {
|
public class AiKnowledgeSegmentSearchReqVO {
|
||||||
|
|
||||||
@Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
|
@Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||||
|
@NotNull(message = "知识库编号不能为空")
|
||||||
private Long knowledgeId;
|
private Long knowledgeId;
|
||||||
|
|
||||||
@Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 学习路线")
|
@Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "如何使用这个产品")
|
||||||
|
@NotEmpty(message = "内容不能为空")
|
||||||
private String content;
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "最大返回数量", example = "5")
|
||||||
|
private Integer topK;
|
||||||
|
|
||||||
|
@Schema(description = "相似度阈值", example = "0.7")
|
||||||
|
private Double similarityThreshold;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - AI 知识库段落搜索 Response VO")
|
||||||
|
@Data
|
||||||
|
public class AiKnowledgeSegmentSearchRespVO extends AiKnowledgeSegmentRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品使用手册")
|
||||||
|
private String documentName;
|
||||||
|
|
||||||
|
@Schema(description = "相似度分数", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.95")
|
||||||
|
private Double score;
|
||||||
|
|
||||||
|
}
|
@ -1,17 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI 更新 知识库-段落 request VO")
|
|
||||||
@Data
|
|
||||||
public class AiKnowledgeSegmentUpdateReqVO {
|
|
||||||
|
|
||||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
|
|
||||||
private String content;
|
|
||||||
|
|
||||||
}
|
|
@ -12,7 +12,6 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.annotation.security.PermitAll;
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
@ -32,7 +31,6 @@ public class AiMindMapController {
|
|||||||
|
|
||||||
@PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
@PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||||
@Operation(summary = "导图生成(流式)", description = "流式返回,响应较快")
|
@Operation(summary = "导图生成(流式)", description = "流式返回,响应较快")
|
||||||
@PermitAll // 解决 SSE 最终响应的时候,会被 Access Denied 拦截的问题
|
|
||||||
public Flux<CommonResult<String>> generateMindMap(@RequestBody @Valid AiMindMapGenerateReqVO generateReqVO) {
|
public Flux<CommonResult<String>> generateMindMap(@RequestBody @Valid AiMindMapGenerateReqVO generateReqVO) {
|
||||||
return mindMapService.generateMindMap(generateReqVO, getLoginUserId());
|
return mindMapService.generateMindMap(generateReqVO, getLoginUserId());
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
|
|||||||
|
|
||||||
@Schema(description = "管理后台 - AI 思维导图分页 Request VO")
|
@Schema(description = "管理后台 - AI 思维导图分页 Request VO")
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@ToString(callSuper = true)
|
|
||||||
public class AiMindMapPageReqVO extends PageParam {
|
public class AiMindMapPageReqVO extends PageParam {
|
||||||
|
|
||||||
@Schema(description = "用户编号", example = "4325")
|
@Schema(description = "用户编号", example = "4325")
|
||||||
|
@ -6,9 +6,8 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|||||||
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyRespVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyRespVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelRespVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelRespVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
|
import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
@ -76,9 +75,9 @@ public class AiApiKeyController {
|
|||||||
|
|
||||||
@GetMapping("/simple-list")
|
@GetMapping("/simple-list")
|
||||||
@Operation(summary = "获得 API 密钥分页列表")
|
@Operation(summary = "获得 API 密钥分页列表")
|
||||||
public CommonResult<List<AiChatModelRespVO>> getApiKeySimpleList() {
|
public CommonResult<List<AiModelRespVO>> getApiKeySimpleList() {
|
||||||
List<AiApiKeyDO> list = apiKeyService.getApiKeyList();
|
List<AiApiKeyDO> list = apiKeyService.getApiKeyList();
|
||||||
return success(convertList(list, key -> new AiChatModelRespVO().setId(key.getId()).setName(key.getName())));
|
return success(convertList(list, key -> new AiModelRespVO().setId(key.getId()).setName(key.getName())));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,84 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.ai.controller.admin.model;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelRespVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import jakarta.validation.Valid;
|
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
|
||||||
|
|
||||||
@Tag(name = "管理后台 - AI 聊天模型")
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/ai/chat-model")
|
|
||||||
@Validated
|
|
||||||
public class AiChatModelController {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private AiChatModelService chatModelService;
|
|
||||||
|
|
||||||
@PostMapping("/create")
|
|
||||||
@Operation(summary = "创建聊天模型")
|
|
||||||
@PreAuthorize("@ss.hasPermission('ai:chat-model:create')")
|
|
||||||
public CommonResult<Long> createChatModel(@Valid @RequestBody AiChatModelSaveReqVO createReqVO) {
|
|
||||||
return success(chatModelService.createChatModel(createReqVO));
|
|
||||||
}
|
|
||||||
|
|
||||||
@PutMapping("/update")
|
|
||||||
@Operation(summary = "更新聊天模型")
|
|
||||||
@PreAuthorize("@ss.hasPermission('ai:chat-model:update')")
|
|
||||||
public CommonResult<Boolean> updateChatModel(@Valid @RequestBody AiChatModelSaveReqVO updateReqVO) {
|
|
||||||
chatModelService.updateChatModel(updateReqVO);
|
|
||||||
return success(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@DeleteMapping("/delete")
|
|
||||||
@Operation(summary = "删除聊天模型")
|
|
||||||
@Parameter(name = "id", description = "编号", required = true)
|
|
||||||
@PreAuthorize("@ss.hasPermission('ai:chat-model:delete')")
|
|
||||||
public CommonResult<Boolean> deleteChatModel(@RequestParam("id") Long id) {
|
|
||||||
chatModelService.deleteChatModel(id);
|
|
||||||
return success(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/get")
|
|
||||||
@Operation(summary = "获得聊天模型")
|
|
||||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
|
||||||
@PreAuthorize("@ss.hasPermission('ai:chat-model:query')")
|
|
||||||
public CommonResult<AiChatModelRespVO> getChatModel(@RequestParam("id") Long id) {
|
|
||||||
AiChatModelDO chatModel = chatModelService.getChatModel(id);
|
|
||||||
return success(BeanUtils.toBean(chatModel, AiChatModelRespVO.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/page")
|
|
||||||
@Operation(summary = "获得聊天模型分页")
|
|
||||||
@PreAuthorize("@ss.hasPermission('ai:chat-model:query')")
|
|
||||||
public CommonResult<PageResult<AiChatModelRespVO>> getChatModelPage(@Valid AiChatModelPageReqVO pageReqVO) {
|
|
||||||
PageResult<AiChatModelDO> pageResult = chatModelService.getChatModelPage(pageReqVO);
|
|
||||||
return success(BeanUtils.toBean(pageResult, AiChatModelRespVO.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/simple-list")
|
|
||||||
@Operation(summary = "获得聊天模型列表")
|
|
||||||
@Parameter(name = "status", description = "状态", required = true, example = "1")
|
|
||||||
public CommonResult<List<AiChatModelRespVO>> getChatModelSimpleList(@RequestParam("status") Integer status) {
|
|
||||||
List<AiChatModelDO> list = chatModelService.getChatModelListByStatus(status);
|
|
||||||
return success(convertList(list, model -> new AiChatModelRespVO().setId(model.getId())
|
|
||||||
.setName(model.getName()).setModel(model.getModel())));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,89 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.controller.admin.model;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelRespVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.model.AiModelService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
|
|
||||||
|
@Tag(name = "管理后台 - AI 模型")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/ai/model")
|
||||||
|
@Validated
|
||||||
|
public class AiModelController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AiModelService modelService;
|
||||||
|
|
||||||
|
@PostMapping("/create")
|
||||||
|
@Operation(summary = "创建模型")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:model:create')")
|
||||||
|
public CommonResult<Long> createModel(@Valid @RequestBody AiModelSaveReqVO createReqVO) {
|
||||||
|
return success(modelService.createModel(createReqVO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update")
|
||||||
|
@Operation(summary = "更新模型")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:model:update')")
|
||||||
|
public CommonResult<Boolean> updateModel(@Valid @RequestBody AiModelSaveReqVO updateReqVO) {
|
||||||
|
modelService.updateModel(updateReqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete")
|
||||||
|
@Operation(summary = "删除模型")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:model:delete')")
|
||||||
|
public CommonResult<Boolean> deleteModel(@RequestParam("id") Long id) {
|
||||||
|
modelService.deleteModel(id);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获得模型")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:model:query')")
|
||||||
|
public CommonResult<AiModelRespVO> getModel(@RequestParam("id") Long id) {
|
||||||
|
AiModelDO model = modelService.getModel(id);
|
||||||
|
return success(BeanUtils.toBean(model, AiModelRespVO.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/page")
|
||||||
|
@Operation(summary = "获得模型分页")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:model:query')")
|
||||||
|
public CommonResult<PageResult<AiModelRespVO>> getModelPage(@Valid AiModelPageReqVO pageReqVO) {
|
||||||
|
PageResult<AiModelDO> pageResult = modelService.getModelPage(pageReqVO);
|
||||||
|
return success(BeanUtils.toBean(pageResult, AiModelRespVO.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/simple-list")
|
||||||
|
@Operation(summary = "获得模型列表")
|
||||||
|
@Parameter(name = "type", description = "类型", required = true, example = "1")
|
||||||
|
@Parameter(name = "platform", description = "平台", example = "midjourney")
|
||||||
|
public CommonResult<List<AiModelRespVO>> getModelSimpleList(
|
||||||
|
@RequestParam("type") Integer type,
|
||||||
|
@RequestParam(value = "platform", required = false) String platform) {
|
||||||
|
List<AiModelDO> list = modelService.getModelListByStatusAndType(
|
||||||
|
CommonStatusEnum.ENABLE.getStatus(), type, platform);
|
||||||
|
return success(convertList(list, model -> new AiModelRespVO().setId(model.getId())
|
||||||
|
.setName(model.getName()).setModel(model.getModel()).setPlatform(model.getPlatform())));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.controller.admin.model;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolRespVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.model.AiToolService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
|
|
||||||
|
@Tag(name = "管理后台 - AI 工具")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/ai/tool")
|
||||||
|
@Validated
|
||||||
|
public class AiToolController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AiToolService toolService;
|
||||||
|
|
||||||
|
@PostMapping("/create")
|
||||||
|
@Operation(summary = "创建工具")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:tool:create')")
|
||||||
|
public CommonResult<Long> createTool(@Valid @RequestBody AiToolSaveReqVO createReqVO) {
|
||||||
|
return success(toolService.createTool(createReqVO));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/update")
|
||||||
|
@Operation(summary = "更新工具")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:tool:update')")
|
||||||
|
public CommonResult<Boolean> updateTool(@Valid @RequestBody AiToolSaveReqVO updateReqVO) {
|
||||||
|
toolService.updateTool(updateReqVO);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/delete")
|
||||||
|
@Operation(summary = "删除工具")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:tool:delete')")
|
||||||
|
public CommonResult<Boolean> deleteTool(@RequestParam("id") Long id) {
|
||||||
|
toolService.deleteTool(id);
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/get")
|
||||||
|
@Operation(summary = "获得工具")
|
||||||
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:tool:query')")
|
||||||
|
public CommonResult<AiToolRespVO> getTool(@RequestParam("id") Long id) {
|
||||||
|
AiToolDO tool = toolService.getTool(id);
|
||||||
|
return success(BeanUtils.toBean(tool, AiToolRespVO.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/page")
|
||||||
|
@Operation(summary = "获得工具分页")
|
||||||
|
@PreAuthorize("@ss.hasPermission('ai:tool:query')")
|
||||||
|
public CommonResult<PageResult<AiToolRespVO>> getToolPage(@Valid AiToolPageReqVO pageReqVO) {
|
||||||
|
PageResult<AiToolDO> pageResult = toolService.getToolPage(pageReqVO);
|
||||||
|
return success(BeanUtils.toBean(pageResult, AiToolRespVO.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/simple-list")
|
||||||
|
@Operation(summary = "获得工具列表")
|
||||||
|
public CommonResult<List<AiToolRespVO>> getToolSimpleList() {
|
||||||
|
List<AiToolDO> list = toolService.getToolListByStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||||
|
return success(convertList(list, tool -> new AiToolRespVO()
|
||||||
|
.setId(tool.getId()).setName(tool.getName())));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;
|
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
import com.fhs.core.trans.anno.Trans;
|
import com.fhs.core.trans.anno.Trans;
|
||||||
import com.fhs.core.trans.constant.TransType;
|
import com.fhs.core.trans.constant.TransType;
|
||||||
import com.fhs.core.trans.vo.VO;
|
import com.fhs.core.trans.vo.VO;
|
||||||
@ -8,6 +8,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI 聊天角色 Response VO")
|
@Schema(description = "管理后台 - AI 聊天角色 Response VO")
|
||||||
@Data
|
@Data
|
||||||
@ -20,7 +21,7 @@ public class AiChatRoleRespVO implements VO {
|
|||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
@Schema(description = "模型编号", example = "17640")
|
@Schema(description = "模型编号", example = "17640")
|
||||||
@Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = {"name", "model"}, refs = {"modelName", "model"})
|
@Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = { "name", "model" }, refs = { "modelName", "model" })
|
||||||
private Long modelId;
|
private Long modelId;
|
||||||
@Schema(description = "模型名字", example = "张三")
|
@Schema(description = "模型名字", example = "张三")
|
||||||
private String modelName;
|
private String modelName;
|
||||||
@ -45,6 +46,12 @@ public class AiChatRoleRespVO implements VO {
|
|||||||
@Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private String systemMessage;
|
private String systemMessage;
|
||||||
|
|
||||||
|
@Schema(description = "引用的知识库编号列表", example = "1,2,3")
|
||||||
|
private List<Long> knowledgeIds;
|
||||||
|
|
||||||
|
@Schema(description = "引用的工具编号列表", example = "1,2,3")
|
||||||
|
private List<Long> toolIds;
|
||||||
|
|
||||||
@Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
private Boolean publicStatus;
|
private Boolean publicStatus;
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ import jakarta.validation.constraints.NotEmpty;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.hibernate.validator.constraints.URL;
|
import org.hibernate.validator.constraints.URL;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI 聊天角色新增/修改【我的】 Request VO")
|
@Schema(description = "管理后台 - AI 聊天角色新增/修改【我的】 Request VO")
|
||||||
@Data
|
@Data
|
||||||
public class AiChatRoleSaveMyReqVO {
|
public class AiChatRoleSaveMyReqVO {
|
||||||
@ -29,4 +31,10 @@ public class AiChatRoleSaveMyReqVO {
|
|||||||
@NotEmpty(message = "角色设定不能为空")
|
@NotEmpty(message = "角色设定不能为空")
|
||||||
private String systemMessage;
|
private String systemMessage;
|
||||||
|
|
||||||
|
@Schema(description = "引用的知识库编号列表", example = "1,2,3")
|
||||||
|
private List<Long> knowledgeIds;
|
||||||
|
|
||||||
|
@Schema(description = "引用的工具编号列表", example = "1,2,3")
|
||||||
|
private List<Long> toolIds;
|
||||||
|
|
||||||
}
|
}
|
@ -7,6 +7,8 @@ import lombok.*;
|
|||||||
import jakarta.validation.constraints.*;
|
import jakarta.validation.constraints.*;
|
||||||
import org.hibernate.validator.constraints.URL;
|
import org.hibernate.validator.constraints.URL;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI 聊天角色新增/修改 Request VO")
|
@Schema(description = "管理后台 - AI 聊天角色新增/修改 Request VO")
|
||||||
@Data
|
@Data
|
||||||
public class AiChatRoleSaveReqVO {
|
public class AiChatRoleSaveReqVO {
|
||||||
@ -42,6 +44,12 @@ public class AiChatRoleSaveReqVO {
|
|||||||
@NotEmpty(message = "角色设定不能为空")
|
@NotEmpty(message = "角色设定不能为空")
|
||||||
private String systemMessage;
|
private String systemMessage;
|
||||||
|
|
||||||
|
@Schema(description = "引用的知识库编号列表", example = "1,2,3")
|
||||||
|
private List<Long> knowledgeIds;
|
||||||
|
|
||||||
|
@Schema(description = "引用的工具编号列表", example = "1,2,3")
|
||||||
|
private List<Long> toolIds;
|
||||||
|
|
||||||
@Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
@NotNull(message = "是否公开不能为空")
|
@NotNull(message = "是否公开不能为空")
|
||||||
private Boolean publicStatus;
|
private Boolean publicStatus;
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel;
|
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
|
||||||
|
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - API 聊天模型分页 Request VO")
|
@Schema(description = "管理后台 - API 模型分页 Request VO")
|
||||||
@Data
|
@Data
|
||||||
public class AiChatModelPageReqVO extends PageParam {
|
public class AiModelPageReqVO extends PageParam {
|
||||||
|
|
||||||
@Schema(description = "模型名字", example = "张三")
|
@Schema(description = "模型名字", example = "张三")
|
||||||
private String name;
|
private String name;
|
@ -1,13 +1,13 @@
|
|||||||
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel;
|
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI 聊天模型 Response VO")
|
@Schema(description = "管理后台 - AI 模型 Response VO")
|
||||||
@Data
|
@Data
|
||||||
public class AiChatModelRespVO {
|
public class AiModelRespVO {
|
||||||
|
|
||||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2630")
|
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2630")
|
||||||
private Long id;
|
private Long id;
|
||||||
@ -24,6 +24,9 @@ public class AiChatModelRespVO {
|
|||||||
@Schema(description = "模型平台", example = "OpenAI")
|
@Schema(description = "模型平台", example = "OpenAI")
|
||||||
private String platform;
|
private String platform;
|
||||||
|
|
||||||
|
@Schema(description = "模型类型", example = "1")
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
@Schema(description = "排序", example = "1")
|
@Schema(description = "排序", example = "1")
|
||||||
private Integer sort;
|
private Integer sort;
|
||||||
|
|
@ -1,14 +1,17 @@
|
|||||||
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel;
|
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum;
|
||||||
|
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.*;
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
import jakarta.validation.constraints.*;
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - API 聊天模型新增/修改 Request VO")
|
@Schema(description = "管理后台 - API 模型新增/修改 Request VO")
|
||||||
@Data
|
@Data
|
||||||
public class AiChatModelSaveReqVO {
|
public class AiModelSaveReqVO {
|
||||||
|
|
||||||
@Schema(description = "编号", example = "2630")
|
@Schema(description = "编号", example = "2630")
|
||||||
private Long id;
|
private Long id;
|
||||||
@ -27,8 +30,14 @@ public class AiChatModelSaveReqVO {
|
|||||||
|
|
||||||
@Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
|
@Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
|
||||||
@NotEmpty(message = "模型平台不能为空")
|
@NotEmpty(message = "模型平台不能为空")
|
||||||
|
@InEnum(AiPlatformEnum.class)
|
||||||
private String platform;
|
private String platform;
|
||||||
|
|
||||||
|
@Schema(description = "模型类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
@NotNull(message = "模型类型不能为空")
|
||||||
|
@InEnum(AiModelTypeEnum.class)
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
@NotNull(message = "排序不能为空")
|
@NotNull(message = "排序不能为空")
|
||||||
private Integer sort;
|
private Integer sort;
|
@ -0,0 +1,34 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||||
|
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - AI 工具分页 Request VO")
|
||||||
|
@Data
|
||||||
|
public class AiToolPageReqVO extends PageParam {
|
||||||
|
|
||||||
|
@Schema(description = "工具名称", example = "王五")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "工具描述", example = "你猜")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "状态", example = "1")
|
||||||
|
@InEnum(CommonStatusEnum.class)
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
private LocalDateTime[] createTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - AI 工具 Response VO")
|
||||||
|
@Data
|
||||||
|
public class AiToolRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "工具编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19661")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "工具名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "工具描述", example = "你猜")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
|
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - AI 工具新增/修改 Request VO")
|
||||||
|
@Data
|
||||||
|
public class AiToolSaveReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "工具编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19661")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "工具名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
|
||||||
|
@NotEmpty(message = "工具名称不能为空")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "工具描述", example = "你猜")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
|
@InEnum(CommonStatusEnum.class)
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
}
|
@ -12,7 +12,6 @@ import io.swagger.v3.oas.annotations.Operation;
|
|||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.annotation.security.PermitAll;
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
@ -32,7 +31,6 @@ public class AiWriteController {
|
|||||||
|
|
||||||
@PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
@PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||||
@Operation(summary = "写作生成(流式)", description = "流式返回,响应较快")
|
@Operation(summary = "写作生成(流式)", description = "流式返回,响应较快")
|
||||||
@PermitAll // 解决 SSE 最终响应的时候,会被 Access Denied 拦截的问题
|
|
||||||
public Flux<CommonResult<String>> generateWriteContent(@RequestBody @Valid AiWriteGenerateReqVO generateReqVO) {
|
public Flux<CommonResult<String>> generateWriteContent(@RequestBody @Valid AiWriteGenerateReqVO generateReqVO) {
|
||||||
return writeService.generateWriteContent(generateReqVO, getLoginUserId());
|
return writeService.generateWriteContent(generateReqVO, getLoginUserId());
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
|
|||||||
|
|
||||||
@Schema(description = "管理后台 - AI 写作分页 Request VO")
|
@Schema(description = "管理后台 - AI 写作分页 Request VO")
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@ToString(callSuper = true)
|
|
||||||
public class AiWritePageReqVO extends PageParam {
|
public class AiWritePageReqVO extends PageParam {
|
||||||
|
|
||||||
@Schema(description = "用户编号", example = "28404")
|
@Schema(description = "用户编号", example = "28404")
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
|
package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
@ -22,7 +21,6 @@ import java.time.LocalDateTime;
|
|||||||
@TableName("ai_chat_conversation")
|
@TableName("ai_chat_conversation")
|
||||||
@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@ -65,21 +63,16 @@ public class AiChatConversationDO extends BaseDO {
|
|||||||
*/
|
*/
|
||||||
private Long roleId;
|
private Long roleId;
|
||||||
|
|
||||||
/**
|
|
||||||
* 知识库编号
|
|
||||||
* <p>
|
|
||||||
* 关联 {@link AiKnowledgeDO#getId()}
|
|
||||||
*/
|
|
||||||
private Long knowledgeId;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模型编号
|
* 模型编号
|
||||||
*
|
*
|
||||||
* 关联 {@link AiChatModelDO#getId()} 字段
|
* 关联 {@link AiModelDO#getId()} 字段
|
||||||
*/
|
*/
|
||||||
private Long modelId;
|
private Long modelId;
|
||||||
/**
|
/**
|
||||||
* 模型标志
|
* 模型标志
|
||||||
|
*
|
||||||
|
* 冗余 {@link AiModelDO#getModel()} 字段
|
||||||
*/
|
*/
|
||||||
private String model;
|
private String model;
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
|
package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import org.springframework.ai.chat.messages.MessageType;
|
import org.springframework.ai.chat.messages.MessageType;
|
||||||
|
|
||||||
@ -20,10 +20,9 @@ import java.util.List;
|
|||||||
* @since 2024/4/14 17:35
|
* @since 2024/4/14 17:35
|
||||||
* @since 2024/4/14 17:35
|
* @since 2024/4/14 17:35
|
||||||
*/
|
*/
|
||||||
@TableName("ai_chat_message")
|
@TableName(value = "ai_chat_message", autoResultMap = true)
|
||||||
@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@ -71,23 +70,16 @@ public class AiChatMessageDO extends BaseDO {
|
|||||||
*/
|
*/
|
||||||
private Long roleId;
|
private Long roleId;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 段落编号数组
|
|
||||||
*
|
|
||||||
* 关联 {@link AiKnowledgeSegmentDO#getId()} 字段
|
|
||||||
*/
|
|
||||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
|
||||||
private List<Long> segmentIds;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模型标志
|
* 模型标志
|
||||||
|
*
|
||||||
|
* 冗余 {@link AiModelDO#getModel()}
|
||||||
*/
|
*/
|
||||||
private String model;
|
private String model;
|
||||||
/**
|
/**
|
||||||
* 模型编号
|
* 模型编号
|
||||||
*
|
*
|
||||||
* 关联 {@link AiChatModelDO#getId()} 字段
|
* 关联 {@link AiModelDO#getId()} 字段
|
||||||
*/
|
*/
|
||||||
private Long modelId;
|
private Long modelId;
|
||||||
|
|
||||||
@ -101,4 +93,12 @@ public class AiChatMessageDO extends BaseDO {
|
|||||||
*/
|
*/
|
||||||
private Boolean useContext;
|
private Boolean useContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 知识库段落编号数组
|
||||||
|
*
|
||||||
|
* 关联 {@link AiKnowledgeSegmentDO#getId()} 字段
|
||||||
|
*/
|
||||||
|
@TableField(typeHandler = LongListTypeHandler.class)
|
||||||
|
private List<Long> segmentIds;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.image;
|
|||||||
|
|
||||||
import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
|
import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;
|
import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;
|
||||||
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
||||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||||
@ -53,9 +53,15 @@ public class AiImageDO extends BaseDO {
|
|||||||
*/
|
*/
|
||||||
private String platform;
|
private String platform;
|
||||||
/**
|
/**
|
||||||
* 模型
|
* 模型编号
|
||||||
*
|
*
|
||||||
* 冗余 {@link AiChatModelDO#getModel()}
|
* 关联 {@link AiModelDO#getId()}
|
||||||
|
*/
|
||||||
|
private Long modelId;
|
||||||
|
/**
|
||||||
|
* 模型标识
|
||||||
|
*
|
||||||
|
* 冗余 {@link AiModelDO#getModel()}
|
||||||
*/
|
*/
|
||||||
private String model;
|
private String model;
|
||||||
|
|
||||||
|
@ -2,15 +2,12 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge;
|
|||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI 知识库 DO
|
* AI 知识库 DO
|
||||||
*
|
*
|
||||||
@ -26,12 +23,6 @@ public class AiKnowledgeDO extends BaseDO {
|
|||||||
*/
|
*/
|
||||||
@TableId
|
@TableId
|
||||||
private Long id;
|
private Long id;
|
||||||
/**
|
|
||||||
* 用户编号
|
|
||||||
* <p>
|
|
||||||
* 关联 AdminUserDO 的 userId 字段
|
|
||||||
*/
|
|
||||||
private Long userId;
|
|
||||||
/**
|
/**
|
||||||
* 知识库名称
|
* 知识库名称
|
||||||
*/
|
*/
|
||||||
@ -42,20 +33,17 @@ public class AiKnowledgeDO extends BaseDO {
|
|||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 可见权限,选择哪些人可见
|
* 向量模型编号
|
||||||
* <p>
|
*
|
||||||
* -1 所有人可见,其他为各自用户编号
|
* 关联 {@link AiModelDO#getId()}
|
||||||
*/
|
*/
|
||||||
@TableField(typeHandler = LongListTypeHandler.class)
|
private Long embeddingModelId;
|
||||||
private List<Long> visibilityPermissions;
|
|
||||||
/**
|
|
||||||
* 嵌入模型编号
|
|
||||||
*/
|
|
||||||
private Long modelId;
|
|
||||||
/**
|
/**
|
||||||
* 模型标识
|
* 模型标识
|
||||||
|
*
|
||||||
|
* 冗余 {@link AiModelDO#getModel()}
|
||||||
*/
|
*/
|
||||||
private String model;
|
private String embeddingModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* topK
|
* topK
|
||||||
|
@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge;
|
|||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum;
|
|
||||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
@ -30,57 +29,35 @@ public class AiKnowledgeDocumentDO extends BaseDO {
|
|||||||
*/
|
*/
|
||||||
private Long knowledgeId;
|
private Long knowledgeId;
|
||||||
/**
|
/**
|
||||||
* 文件名称
|
* 文档名称
|
||||||
*/
|
*/
|
||||||
private String name;
|
private String name;
|
||||||
/**
|
|
||||||
* 内容
|
|
||||||
*/
|
|
||||||
private String content;
|
|
||||||
/**
|
/**
|
||||||
* 文件 URL
|
* 文件 URL
|
||||||
*/
|
*/
|
||||||
private String url;
|
private String url;
|
||||||
|
/**
|
||||||
|
* 内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
/**
|
||||||
|
* 文档长度
|
||||||
|
*/
|
||||||
|
private Integer contentLength;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文档 token 数量
|
* 文档 token 数量
|
||||||
*/
|
*/
|
||||||
private Integer tokens;
|
private Integer tokens;
|
||||||
/**
|
/**
|
||||||
* 文档字符数
|
* 分片最大 Token 数
|
||||||
*/
|
*/
|
||||||
private Integer wordCount;
|
private Integer segmentMaxTokens;
|
||||||
|
|
||||||
|
|
||||||
// ========== 自定义分段所用参数 ==========
|
|
||||||
// TODO @新:3)defaultChunkSize、defaultChunkSize、minChunkSizeChars、maxNumChunks 这几个字段的命名,可能要微信一起讨论下。尽量命名保持风格统一哈。
|
|
||||||
/**
|
|
||||||
* 每个文本块的目标 token 数
|
|
||||||
*/
|
|
||||||
private Integer defaultSegmentTokens;
|
|
||||||
/**
|
|
||||||
* 每个文本块的最小字符数
|
|
||||||
*/
|
|
||||||
private Integer minSegmentWordCount;
|
|
||||||
/**
|
|
||||||
* 低于此值的块会被丢弃
|
|
||||||
*/
|
|
||||||
private Integer minChunkLengthToEmbed;
|
|
||||||
/**
|
|
||||||
* 最大块数
|
|
||||||
*/
|
|
||||||
private Integer maxNumSegments;
|
|
||||||
/**
|
|
||||||
* 分块是否保留分隔符
|
|
||||||
*/
|
|
||||||
private Boolean keepSeparator;
|
|
||||||
// ===================================
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 切片状态
|
* 召回次数
|
||||||
* <p>
|
|
||||||
* 枚举 {@link AiKnowledgeDocumentStatusEnum}
|
|
||||||
*/
|
*/
|
||||||
private Integer sliceStatus;
|
private Integer retrievalCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 状态
|
* 状态
|
||||||
|
@ -17,17 +17,16 @@ import lombok.Data;
|
|||||||
@Data
|
@Data
|
||||||
public class AiKnowledgeSegmentDO extends BaseDO {
|
public class AiKnowledgeSegmentDO extends BaseDO {
|
||||||
|
|
||||||
public static final String FIELD_KNOWLEDGE_ID = "knowledgeId";
|
/**
|
||||||
|
* 向量库的编号 - 空值
|
||||||
|
*/
|
||||||
|
public static final String VECTOR_ID_EMPTY = "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编号
|
* 编号
|
||||||
*/
|
*/
|
||||||
@TableId
|
@TableId
|
||||||
private Long id;
|
private Long id;
|
||||||
/**
|
|
||||||
* 向量库的编号
|
|
||||||
*/
|
|
||||||
private String vectorId;
|
|
||||||
/**
|
/**
|
||||||
* 知识库编号
|
* 知识库编号
|
||||||
* <p>
|
* <p>
|
||||||
@ -45,13 +44,24 @@ public class AiKnowledgeSegmentDO extends BaseDO {
|
|||||||
*/
|
*/
|
||||||
private String content;
|
private String content;
|
||||||
/**
|
/**
|
||||||
* 字符数
|
* 切片内容长度
|
||||||
*/
|
*/
|
||||||
private Integer wordCount;
|
private Integer contentLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向量库的编号
|
||||||
|
*/
|
||||||
|
private String vectorId;
|
||||||
/**
|
/**
|
||||||
* token 数量
|
* token 数量
|
||||||
*/
|
*/
|
||||||
private Integer tokens;
|
private Integer tokens;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 召回次数
|
||||||
|
*/
|
||||||
|
private Integer retrievalCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 状态
|
* 状态
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.mindmap;
|
|||||||
|
|
||||||
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
@ -36,6 +37,12 @@ public class AiMindMapDO extends BaseDO {
|
|||||||
* 枚举 {@link AiPlatformEnum}
|
* 枚举 {@link AiPlatformEnum}
|
||||||
*/
|
*/
|
||||||
private String platform;
|
private String platform;
|
||||||
|
/**
|
||||||
|
* 模型编号
|
||||||
|
*
|
||||||
|
* 关联 {@link AiModelDO#getId()}
|
||||||
|
*/
|
||||||
|
private Long modelId;
|
||||||
/**
|
/**
|
||||||
* 模型
|
* 模型
|
||||||
*/
|
*/
|
||||||
|
@ -16,7 +16,6 @@ import lombok.*;
|
|||||||
@TableName("ai_api_key")
|
@TableName("ai_api_key")
|
||||||
@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@ -2,11 +2,16 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.model;
|
|||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
|
||||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI 聊天角色 DO
|
* AI 聊天角色 DO
|
||||||
*
|
*
|
||||||
@ -16,7 +21,6 @@ import lombok.*;
|
|||||||
@TableName(value = "ai_chat_role", autoResultMap = true)
|
@TableName(value = "ai_chat_role", autoResultMap = true)
|
||||||
@KeySequence("ai_chat_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
@KeySequence("ai_chat_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@ -58,10 +62,25 @@ public class AiChatRoleDO extends BaseDO {
|
|||||||
/**
|
/**
|
||||||
* 模型编号
|
* 模型编号
|
||||||
*
|
*
|
||||||
* 关联 {@link AiChatModelDO#getId()} 字段
|
* 关联 {@link AiModelDO#getId()} 字段
|
||||||
*/
|
*/
|
||||||
private Long modelId;
|
private Long modelId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 引用的知识库编号列表
|
||||||
|
*
|
||||||
|
* 关联 {@link AiKnowledgeDO#getId()} 字段
|
||||||
|
*/
|
||||||
|
@TableField(typeHandler = LongListTypeHandler.class)
|
||||||
|
private List<Long> knowledgeIds;
|
||||||
|
/**
|
||||||
|
* 引用的工具编号列表
|
||||||
|
*
|
||||||
|
* 关联 {@link AiToolDO#getId()} 字段
|
||||||
|
*/
|
||||||
|
@TableField(typeHandler = LongListTypeHandler.class)
|
||||||
|
private List<Long> toolIds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否公开
|
* 是否公开
|
||||||
*
|
*
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package cn.iocoder.yudao.module.ai.dal.dataobject.model;
|
package cn.iocoder.yudao.module.ai.dal.dataobject.model;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum;
|
||||||
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
@ -9,21 +10,20 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
|||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI 聊天模型 DO
|
* AI 模型 DO
|
||||||
*
|
*
|
||||||
* 默认聊天模型:{@link #status} 为开启,并且 {@link #sort} 排序第一
|
* 默认模型:{@link #status} 为开启,并且 {@link #sort} 排序第一
|
||||||
*
|
*
|
||||||
* @author fansili
|
* @author fansili
|
||||||
* @since 2024/4/24 19:39
|
* @since 2024/4/24 19:39
|
||||||
*/
|
*/
|
||||||
@TableName("ai_chat_model")
|
@TableName("ai_model")
|
||||||
@KeySequence("ai_chat_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
@KeySequence("ai_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class AiChatModelDO extends BaseDO {
|
public class AiModelDO extends BaseDO {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编号
|
* 编号
|
||||||
@ -50,6 +50,12 @@ public class AiChatModelDO extends BaseDO {
|
|||||||
* 枚举 {@link AiPlatformEnum}
|
* 枚举 {@link AiPlatformEnum}
|
||||||
*/
|
*/
|
||||||
private String platform;
|
private String platform;
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*
|
||||||
|
* 枚举 {@link AiModelTypeEnum}
|
||||||
|
*/
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 排序值
|
* 排序值
|
@ -0,0 +1,48 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.dal.dataobject.model;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.model.tool.DirectoryListToolFunction;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.model.tool.WeatherQueryToolFunction;
|
||||||
|
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 工具 DO
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@TableName("ai_tool")
|
||||||
|
@KeySequence("ai_tool_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class AiToolDO extends BaseDO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具编号
|
||||||
|
*/
|
||||||
|
@TableId
|
||||||
|
private Long id;
|
||||||
|
/**
|
||||||
|
* 工具名称
|
||||||
|
*
|
||||||
|
* 对应 Bean 的名字,例如说:
|
||||||
|
* 1. {@link DirectoryListToolFunction} 的 Bean 名字是 directory_list
|
||||||
|
* 2. {@link WeatherQueryToolFunction} 的 Bean 名字是 weather_query
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
/**
|
||||||
|
* 工具描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
/**
|
||||||
|
* 状态
|
||||||
|
*
|
||||||
|
* 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}
|
||||||
|
*/
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
}
|
@ -84,6 +84,7 @@ public class AiMusicDO extends BaseDO {
|
|||||||
* 枚举 {@link AiPlatformEnum}
|
* 枚举 {@link AiPlatformEnum}
|
||||||
*/
|
*/
|
||||||
private String platform;
|
private String platform;
|
||||||
|
// TODO @芋艿:modelId?
|
||||||
/**
|
/**
|
||||||
* 模型
|
* 模型
|
||||||
*/
|
*/
|
||||||
|
@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.write;
|
|||||||
|
|
||||||
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.enums.DictTypeConstants;
|
||||||
import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;
|
import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;
|
||||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
@ -44,6 +46,12 @@ public class AiWriteDO extends BaseDO {
|
|||||||
* 枚举 {@link AiPlatformEnum}
|
* 枚举 {@link AiPlatformEnum}
|
||||||
*/
|
*/
|
||||||
private String platform;
|
private String platform;
|
||||||
|
/**
|
||||||
|
* 模型编号
|
||||||
|
*
|
||||||
|
* 关联 {@link AiModelDO#getId()}
|
||||||
|
*/
|
||||||
|
private Long modelId;
|
||||||
/**
|
/**
|
||||||
* 模型
|
* 模型
|
||||||
*/
|
*/
|
||||||
@ -66,25 +74,25 @@ public class AiWriteDO extends BaseDO {
|
|||||||
/**
|
/**
|
||||||
* 长度提示词
|
* 长度提示词
|
||||||
*
|
*
|
||||||
* 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_LENGTH}
|
* 字典:{@link DictTypeConstants#AI_WRITE_LENGTH}
|
||||||
*/
|
*/
|
||||||
private Integer length;
|
private Integer length;
|
||||||
/**
|
/**
|
||||||
* 格式提示词
|
* 格式提示词
|
||||||
*
|
*
|
||||||
* 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_FORMAT}
|
* 字典:{@link DictTypeConstants#AI_WRITE_FORMAT}
|
||||||
*/
|
*/
|
||||||
private Integer format;
|
private Integer format;
|
||||||
/**
|
/**
|
||||||
* 语气提示词
|
* 语气提示词
|
||||||
*
|
*
|
||||||
* 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_TONE}
|
* 字典:{@link DictTypeConstants#AI_WRITE_TONE}
|
||||||
*/
|
*/
|
||||||
private Integer tone;
|
private Integer tone;
|
||||||
/**
|
/**
|
||||||
* 语言提示词
|
* 语言提示词
|
||||||
*
|
*
|
||||||
* 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_LANGUAGE}
|
* 字典:{@link DictTypeConstants#AI_WRITE_LANGUAGE}
|
||||||
*/
|
*/
|
||||||
private Integer language;
|
private Integer language;
|
||||||
|
|
||||||
|
@ -5,10 +5,14 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
|||||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI 知识库-文档 Mapper
|
* AI 知识库文档 Mapper
|
||||||
*
|
*
|
||||||
* @author xiaoxin
|
* @author xiaoxin
|
||||||
*/
|
*/
|
||||||
@ -17,8 +21,19 @@ public interface AiKnowledgeDocumentMapper extends BaseMapperX<AiKnowledgeDocume
|
|||||||
|
|
||||||
default PageResult<AiKnowledgeDocumentDO> selectPage(AiKnowledgeDocumentPageReqVO reqVO) {
|
default PageResult<AiKnowledgeDocumentDO> selectPage(AiKnowledgeDocumentPageReqVO reqVO) {
|
||||||
return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeDocumentDO>()
|
return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeDocumentDO>()
|
||||||
|
.eqIfPresent(AiKnowledgeDocumentDO::getKnowledgeId, reqVO.getKnowledgeId())
|
||||||
.likeIfPresent(AiKnowledgeDocumentDO::getName, reqVO.getName())
|
.likeIfPresent(AiKnowledgeDocumentDO::getName, reqVO.getName())
|
||||||
.orderByDesc(AiKnowledgeDocumentDO::getId));
|
.orderByDesc(AiKnowledgeDocumentDO::getId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void updateRetrievalCountIncr(Collection<Long> ids) {
|
||||||
|
update(new LambdaUpdateWrapper<AiKnowledgeDocumentDO>()
|
||||||
|
.setSql(" retrieval_count = retrieval_count + 1")
|
||||||
|
.in(AiKnowledgeDocumentDO::getId, ids));
|
||||||
|
}
|
||||||
|
|
||||||
|
default List<AiKnowledgeDocumentDO> selectListByStatus(Integer status) {
|
||||||
|
return selectList(AiKnowledgeDocumentDO::getStatus, status);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package cn.iocoder.yudao.module.ai.dal.mysql.knowledge;
|
package cn.iocoder.yudao.module.ai.dal.mysql.knowledge;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
@ -8,19 +7,26 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnow
|
|||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI 知识库基础信息 Mapper
|
* AI 知识库 Mapper
|
||||||
*
|
*
|
||||||
* @author xiaoxin
|
* @author xiaoxin
|
||||||
*/
|
*/
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface AiKnowledgeMapper extends BaseMapperX<AiKnowledgeDO> {
|
public interface AiKnowledgeMapper extends BaseMapperX<AiKnowledgeDO> {
|
||||||
|
|
||||||
default PageResult<AiKnowledgeDO> selectPage(Long userId, AiKnowledgePageReqVO pageReqVO) {
|
default PageResult<AiKnowledgeDO> selectPage(AiKnowledgePageReqVO pageReqVO) {
|
||||||
return selectPage(pageReqVO, new LambdaQueryWrapperX<AiKnowledgeDO>()
|
return selectPage(pageReqVO, new LambdaQueryWrapperX<AiKnowledgeDO>()
|
||||||
.eq(AiKnowledgeDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
|
|
||||||
.likeIfPresent(AiKnowledgeDO::getName, pageReqVO.getName())
|
.likeIfPresent(AiKnowledgeDO::getName, pageReqVO.getName())
|
||||||
.and(e -> e.apply("FIND_IN_SET(" + userId + ",visibility_permissions)").or(m -> m.apply("FIND_IN_SET(-1,visibility_permissions)")))
|
.eqIfPresent(AiKnowledgeDO::getStatus, pageReqVO.getStatus())
|
||||||
|
.betweenIfPresent(AiKnowledgeDO::getCreateTime, pageReqVO.getCreateTime())
|
||||||
.orderByDesc(AiKnowledgeDO::getId));
|
.orderByDesc(AiKnowledgeDO::getId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default List<AiKnowledgeDO> selectListByStatus(Integer status) {
|
||||||
|
return selectList(AiKnowledgeDO::getStatus, status);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,19 @@ package cn.iocoder.yudao.module.ai.dal.mysql.knowledge;
|
|||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
|
import com.github.yulichang.wrapper.MPJLambdaWrapper;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI 知识库-分片 Mapper
|
* AI 知识库分片 Mapper
|
||||||
*
|
*
|
||||||
* @author xiaoxin
|
* @author xiaoxin
|
||||||
*/
|
*/
|
||||||
@ -20,8 +25,8 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX<AiKnowledgeSegment
|
|||||||
default PageResult<AiKnowledgeSegmentDO> selectPage(AiKnowledgeSegmentPageReqVO reqVO) {
|
default PageResult<AiKnowledgeSegmentDO> selectPage(AiKnowledgeSegmentPageReqVO reqVO) {
|
||||||
return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()
|
return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()
|
||||||
.eq(AiKnowledgeSegmentDO::getDocumentId, reqVO.getDocumentId())
|
.eq(AiKnowledgeSegmentDO::getDocumentId, reqVO.getDocumentId())
|
||||||
|
.likeIfPresent(AiKnowledgeSegmentDO::getContent, reqVO.getContent())
|
||||||
.eqIfPresent(AiKnowledgeSegmentDO::getStatus, reqVO.getStatus())
|
.eqIfPresent(AiKnowledgeSegmentDO::getStatus, reqVO.getStatus())
|
||||||
.likeIfPresent(AiKnowledgeSegmentDO::getContent, reqVO.getKeyword())
|
|
||||||
.orderByDesc(AiKnowledgeSegmentDO::getId));
|
.orderByDesc(AiKnowledgeSegmentDO::getId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,4 +36,32 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX<AiKnowledgeSegment
|
|||||||
.orderByDesc(AiKnowledgeSegmentDO::getId));
|
.orderByDesc(AiKnowledgeSegmentDO::getId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default List<AiKnowledgeSegmentDO> selectListByDocumentId(Long documentId) {
|
||||||
|
return selectList(new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()
|
||||||
|
.eq(AiKnowledgeSegmentDO::getDocumentId, documentId)
|
||||||
|
.orderByDesc(AiKnowledgeSegmentDO::getId));
|
||||||
|
}
|
||||||
|
|
||||||
|
default List<AiKnowledgeSegmentDO> selectListByKnowledgeIdAndStatus(Long knowledgeId, Integer status) {
|
||||||
|
return selectList(AiKnowledgeSegmentDO::getKnowledgeId, knowledgeId,
|
||||||
|
AiKnowledgeSegmentDO::getStatus, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
default List<AiKnowledgeSegmentProcessRespVO> selectProcessList(Collection<Long> documentIds) {
|
||||||
|
MPJLambdaWrapper<AiKnowledgeSegmentDO> wrapper = new MPJLambdaWrapperX<AiKnowledgeSegmentDO>()
|
||||||
|
.selectAs(AiKnowledgeSegmentDO::getDocumentId, AiKnowledgeSegmentProcessRespVO::getDocumentId)
|
||||||
|
.selectCount(AiKnowledgeSegmentDO::getId, "count")
|
||||||
|
.select("COUNT(CASE WHEN vector_id > '" + AiKnowledgeSegmentDO.VECTOR_ID_EMPTY
|
||||||
|
+ "' THEN 1 ELSE NULL END) AS embeddingCount")
|
||||||
|
.in(AiKnowledgeSegmentDO::getDocumentId, documentIds)
|
||||||
|
.groupBy(AiKnowledgeSegmentDO::getDocumentId);
|
||||||
|
return selectJoinList(AiKnowledgeSegmentProcessRespVO.class, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void updateRetrievalCountIncrByIds(List<Long> ids) {
|
||||||
|
update(new LambdaUpdateWrapper<AiKnowledgeSegmentDO>()
|
||||||
|
.setSql(" retrieval_count = retrieval_count + 1")
|
||||||
|
.in(AiKnowledgeSegmentDO::getId, ids));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.dal.mysql.model;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API 模型 Mapper
|
||||||
|
*
|
||||||
|
* @author fansili
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface AiChatMapper extends BaseMapperX<AiModelDO> {
|
||||||
|
|
||||||
|
default AiModelDO selectFirstByStatus(Integer type, Integer status) {
|
||||||
|
return selectOne(new QueryWrapperX<AiModelDO>()
|
||||||
|
.eq("type", type)
|
||||||
|
.eq("status", status)
|
||||||
|
.limitN(1)
|
||||||
|
.orderByAsc("sort"));
|
||||||
|
}
|
||||||
|
|
||||||
|
default PageResult<AiModelDO> selectPage(AiModelPageReqVO reqVO) {
|
||||||
|
return selectPage(reqVO, new LambdaQueryWrapperX<AiModelDO>()
|
||||||
|
.likeIfPresent(AiModelDO::getName, reqVO.getName())
|
||||||
|
.eqIfPresent(AiModelDO::getModel, reqVO.getModel())
|
||||||
|
.eqIfPresent(AiModelDO::getPlatform, reqVO.getPlatform())
|
||||||
|
.orderByAsc(AiModelDO::getSort));
|
||||||
|
}
|
||||||
|
|
||||||
|
default List<AiModelDO> selectListByStatusAndType(Integer status, Integer type,
|
||||||
|
@Nullable String platform) {
|
||||||
|
return selectList(new LambdaQueryWrapperX<AiModelDO>()
|
||||||
|
.eq(AiModelDO::getStatus, status)
|
||||||
|
.eq(AiModelDO::getType, type)
|
||||||
|
.eqIfPresent(AiModelDO::getPlatform, platform)
|
||||||
|
.orderByAsc(AiModelDO::getSort));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,43 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.ai.dal.mysql.model;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
|
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* API 聊天模型 Mapper
|
|
||||||
*
|
|
||||||
* @author fansili
|
|
||||||
*/
|
|
||||||
@Mapper
|
|
||||||
public interface AiChatModelMapper extends BaseMapperX<AiChatModelDO> {
|
|
||||||
|
|
||||||
default AiChatModelDO selectFirstByStatus(Integer status) {
|
|
||||||
return selectOne(new QueryWrapperX<AiChatModelDO>()
|
|
||||||
.eq("status", status)
|
|
||||||
.limitN(1)
|
|
||||||
.orderByAsc("sort"));
|
|
||||||
}
|
|
||||||
|
|
||||||
default PageResult<AiChatModelDO> selectPage(AiChatModelPageReqVO reqVO) {
|
|
||||||
return selectPage(reqVO, new LambdaQueryWrapperX<AiChatModelDO>()
|
|
||||||
.likeIfPresent(AiChatModelDO::getName, reqVO.getName())
|
|
||||||
.eqIfPresent(AiChatModelDO::getModel, reqVO.getModel())
|
|
||||||
.eqIfPresent(AiChatModelDO::getPlatform, reqVO.getPlatform())
|
|
||||||
.orderByAsc(AiChatModelDO::getSort));
|
|
||||||
}
|
|
||||||
|
|
||||||
default List<AiChatModelDO> selectList(Integer status) {
|
|
||||||
return selectList(new LambdaQueryWrapperX<AiChatModelDO>()
|
|
||||||
.eq(AiChatModelDO::getStatus, status)
|
|
||||||
.orderByAsc(AiChatModelDO::getSort));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,35 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.dal.mysql.model;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 工具 Mapper
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface AiToolMapper extends BaseMapperX<AiToolDO> {
|
||||||
|
|
||||||
|
default PageResult<AiToolDO> selectPage(AiToolPageReqVO reqVO) {
|
||||||
|
return selectPage(reqVO, new LambdaQueryWrapperX<AiToolDO>()
|
||||||
|
.likeIfPresent(AiToolDO::getName, reqVO.getName())
|
||||||
|
.eqIfPresent(AiToolDO::getDescription, reqVO.getDescription())
|
||||||
|
.eqIfPresent(AiToolDO::getStatus, reqVO.getStatus())
|
||||||
|
.betweenIfPresent(AiToolDO::getCreateTime, reqVO.getCreateTime())
|
||||||
|
.orderByDesc(AiToolDO::getId));
|
||||||
|
}
|
||||||
|
|
||||||
|
default List<AiToolDO> selectListByStatus(Integer status) {
|
||||||
|
return selectList(new LambdaQueryWrapperX<AiToolDO>()
|
||||||
|
.eq(AiToolDO::getStatus, status)
|
||||||
|
.orderByDesc(AiToolDO::getId));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
/**
|
/**
|
||||||
* crm 模块的 web 拓展封装
|
* ai 模块的 web 拓展封装
|
||||||
*/
|
*/
|
||||||
package cn.iocoder.yudao.module.crm.framework.web;
|
package cn.iocoder.yudao.module.ai.framework.web;
|
||||||
|
@ -4,17 +4,18 @@ import cn.hutool.core.collection.CollUtil;
|
|||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.ObjUtil;
|
import cn.hutool.core.util.ObjUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum;
|
||||||
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.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper;
|
import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper;
|
||||||
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
|
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
|
import cn.iocoder.yudao.module.ai.service.model.AiModelService;
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
|
import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -44,7 +45,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
|
|||||||
private AiChatConversationMapper chatConversationMapper;
|
private AiChatConversationMapper chatConversationMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiChatModelService chatModalService;
|
private AiModelService modalService;
|
||||||
@Resource
|
@Resource
|
||||||
private AiChatRoleService chatRoleService;
|
private AiChatRoleService chatRoleService;
|
||||||
@Resource
|
@Resource
|
||||||
@ -54,9 +55,9 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
|
|||||||
public Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId) {
|
public Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId) {
|
||||||
// 1.1 获得 AiChatRoleDO 聊天角色
|
// 1.1 获得 AiChatRoleDO 聊天角色
|
||||||
AiChatRoleDO role = createReqVO.getRoleId() != null ? chatRoleService.validateChatRole(createReqVO.getRoleId()) : null;
|
AiChatRoleDO role = createReqVO.getRoleId() != null ? chatRoleService.validateChatRole(createReqVO.getRoleId()) : null;
|
||||||
// 1.2 获得 AiChatModelDO 聊天模型
|
// 1.2 获得 AiModelDO 聊天模型
|
||||||
AiChatModelDO model = role != null && role.getModelId() != null ? chatModalService.validateChatModel(role.getModelId())
|
AiModelDO model = role != null && role.getModelId() != null ? modalService.validateModel(role.getModelId())
|
||||||
: chatModalService.getRequiredDefaultChatModel();
|
: modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());
|
||||||
Assert.notNull(model, "必须找到默认模型");
|
Assert.notNull(model, "必须找到默认模型");
|
||||||
validateChatModel(model);
|
validateChatModel(model);
|
||||||
|
|
||||||
@ -67,7 +68,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
|
|||||||
|
|
||||||
// 2. 创建 AiChatConversationDO 聊天对话
|
// 2. 创建 AiChatConversationDO 聊天对话
|
||||||
AiChatConversationDO conversation = new AiChatConversationDO().setUserId(userId).setPinned(false)
|
AiChatConversationDO conversation = new AiChatConversationDO().setUserId(userId).setPinned(false)
|
||||||
.setModelId(model.getId()).setModel(model.getModel()).setKnowledgeId(createReqVO.getKnowledgeId())
|
.setModelId(model.getId()).setModel(model.getModel())
|
||||||
.setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts());
|
.setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts());
|
||||||
if (role != null) {
|
if (role != null) {
|
||||||
conversation.setTitle(role.getName()).setRoleId(role.getId()).setSystemMessage(role.getSystemMessage());
|
conversation.setTitle(role.getName()).setRoleId(role.getId()).setSystemMessage(role.getSystemMessage());
|
||||||
@ -86,9 +87,9 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
|
|||||||
throw exception(CHAT_CONVERSATION_NOT_EXISTS);
|
throw exception(CHAT_CONVERSATION_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
// 1.2 校验模型是否存在(修改模型的情况)
|
// 1.2 校验模型是否存在(修改模型的情况)
|
||||||
AiChatModelDO model = null;
|
AiModelDO model = null;
|
||||||
if (updateReqVO.getModelId() != null) {
|
if (updateReqVO.getModelId() != null) {
|
||||||
model = chatModalService.validateChatModel(updateReqVO.getModelId());
|
model = modalService.validateModel(updateReqVO.getModelId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1.3 校验知识库是否存在
|
// 1.3 校验知识库是否存在
|
||||||
@ -139,10 +140,11 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
|
|||||||
chatConversationMapper.deleteById(id);
|
chatConversationMapper.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateChatModel(AiChatModelDO model) {
|
private void validateChatModel(AiModelDO model) {
|
||||||
if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) {
|
if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Assert.equals(model.getType(), AiModelTypeEnum.CHAT.getType(), "模型类型不正确:" + model);
|
||||||
throw exception(CHAT_CONVERSATION_MODEL_ERROR);
|
throw exception(CHAT_CONVERSATION_MODEL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,19 +10,24 @@ 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.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.AiChatMessageRespVO;
|
||||||
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;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper;
|
import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper;
|
||||||
import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;
|
|
||||||
import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
|
import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
|
||||||
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
|
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
|
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
|
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.model.AiModelService;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.model.AiToolService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.ai.chat.messages.Message;
|
import org.springframework.ai.chat.messages.Message;
|
||||||
@ -34,18 +39,19 @@ import org.springframework.ai.chat.model.ChatResponse;
|
|||||||
import org.springframework.ai.chat.model.StreamingChatModel;
|
import org.springframework.ai.chat.model.StreamingChatModel;
|
||||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||||
import org.springframework.ai.chat.prompt.Prompt;
|
import org.springframework.ai.chat.prompt.Prompt;
|
||||||
import org.springframework.ai.chat.prompt.PromptTemplate;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||||
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;
|
||||||
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_NOT_EXIST;
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_NOT_EXIST;
|
||||||
|
|
||||||
@ -58,138 +64,199 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_N
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class AiChatMessageServiceImpl implements AiChatMessageService {
|
public class AiChatMessageServiceImpl implements AiChatMessageService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 知识库转 {@link UserMessage} 的内容模版
|
||||||
|
*/
|
||||||
|
private static final String KNOWLEDGE_USER_MESSAGE_TEMPLATE = "使用 <Reference></Reference> 标记中的内容作为本次对话的参考:\n\n" +
|
||||||
|
"%s\n\n" + // 多个 <Reference></Reference> 的拼接
|
||||||
|
"回答要求:\n- 避免提及你是从 <Reference></Reference> 获取的知识。";
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiChatMessageMapper chatMessageMapper;
|
private AiChatMessageMapper chatMessageMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiChatConversationService chatConversationService;
|
private AiChatConversationService chatConversationService;
|
||||||
@Resource
|
@Resource
|
||||||
private AiChatModelService chatModalService;
|
private AiChatRoleService chatRoleService;
|
||||||
@Resource
|
@Resource
|
||||||
private AiApiKeyService apiKeyService;
|
private AiModelService modalService;
|
||||||
@Resource
|
@Resource
|
||||||
private AiKnowledgeSegmentService knowledgeSegmentService;
|
private AiKnowledgeSegmentService knowledgeSegmentService;
|
||||||
|
@Resource
|
||||||
|
private AiKnowledgeDocumentService knowledgeDocumentService;
|
||||||
|
@Resource
|
||||||
|
private AiToolService toolService;
|
||||||
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) {
|
public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) {
|
||||||
// 1.1 校验对话存在
|
// 1.1 校验对话存在
|
||||||
AiChatConversationDO conversation = chatConversationService.validateChatConversationExists(sendReqVO.getConversationId());
|
AiChatConversationDO conversation = chatConversationService
|
||||||
|
.validateChatConversationExists(sendReqVO.getConversationId());
|
||||||
if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
|
if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
|
||||||
throw exception(CHAT_CONVERSATION_NOT_EXISTS);
|
throw exception(CHAT_CONVERSATION_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());
|
List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());
|
||||||
// 1.2 校验模型
|
// 1.2 校验模型
|
||||||
AiChatModelDO model = chatModalService.validateChatModel(conversation.getModelId());
|
AiModelDO model = modalService.validateModel(conversation.getModelId());
|
||||||
ChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
|
ChatModel chatModel = modalService.getChatModel(model.getId());
|
||||||
|
|
||||||
// 2. 插入 user 发送消息
|
// 2. 知识库找回
|
||||||
|
List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(),
|
||||||
|
conversation);
|
||||||
|
|
||||||
|
// 3. 插入 user 发送消息
|
||||||
AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
|
AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
|
||||||
userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext());
|
userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(),
|
||||||
|
null);
|
||||||
|
|
||||||
// 3.1 插入 assistant 接收消息
|
// 3.1 插入 assistant 接收消息
|
||||||
AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
|
AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
|
||||||
userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext());
|
userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(),
|
||||||
|
knowledgeSegments);
|
||||||
|
|
||||||
// 3.2 召回段落
|
// 3.2 创建 chat 需要的 Prompt
|
||||||
List<AiKnowledgeSegmentDO> segmentList = recallSegment(sendReqVO.getContent(), conversation.getKnowledgeId());
|
Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO);
|
||||||
|
|
||||||
// 3.3 创建 chat 需要的 Prompt
|
|
||||||
Prompt prompt = buildPrompt(conversation, historyMessages, segmentList, model, sendReqVO);
|
|
||||||
ChatResponse chatResponse = chatModel.call(prompt);
|
ChatResponse chatResponse = chatModel.call(prompt);
|
||||||
|
|
||||||
// 3.4 段式返回
|
// 3.3 更新响应内容
|
||||||
String newContent = chatResponse.getResult().getOutput().getContent();
|
String newContent = chatResponse.getResult().getOutput().getText();
|
||||||
chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId)).setContent(newContent));
|
chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(newContent));
|
||||||
return new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
|
// 3.4 响应结果
|
||||||
.setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent));
|
List<AiChatMessageRespVO.KnowledgeSegment> segments = BeanUtils.toBean(knowledgeSegments,
|
||||||
|
AiChatMessageRespVO.KnowledgeSegment.class,
|
||||||
|
segment -> {
|
||||||
|
AiKnowledgeDocumentDO document = knowledgeDocumentService
|
||||||
|
.getKnowledgeDocument(segment.getDocumentId());
|
||||||
|
segment.setDocumentName(document != null ? document.getName() : null);
|
||||||
|
});
|
||||||
|
return new AiChatMessageSendRespVO()
|
||||||
|
.setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
|
||||||
|
.setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
|
||||||
|
.setContent(newContent).setSegments(segments));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId) {
|
public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO,
|
||||||
|
Long userId) {
|
||||||
// 1.1 校验对话存在
|
// 1.1 校验对话存在
|
||||||
AiChatConversationDO conversation = chatConversationService.validateChatConversationExists(sendReqVO.getConversationId());
|
AiChatConversationDO conversation = chatConversationService
|
||||||
|
.validateChatConversationExists(sendReqVO.getConversationId());
|
||||||
if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
|
if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
|
||||||
throw exception(CHAT_CONVERSATION_NOT_EXISTS);
|
throw exception(CHAT_CONVERSATION_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());
|
List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());
|
||||||
// 1.2 校验模型
|
// 1.2 校验模型
|
||||||
AiChatModelDO model = chatModalService.validateChatModel(conversation.getModelId());
|
AiModelDO model = modalService.validateModel(conversation.getModelId());
|
||||||
StreamingChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
|
StreamingChatModel chatModel = modalService.getChatModel(model.getId());
|
||||||
|
|
||||||
// 2. 插入 user 发送消息
|
// 2. 知识库找回
|
||||||
|
List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(),
|
||||||
|
conversation);
|
||||||
|
|
||||||
|
// 3. 插入 user 发送消息
|
||||||
AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
|
AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
|
||||||
userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext());
|
userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(),
|
||||||
|
null);
|
||||||
|
|
||||||
// 3.1 插入 assistant 接收消息
|
// 4.1 插入 assistant 接收消息
|
||||||
AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
|
AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
|
||||||
userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext());
|
userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(),
|
||||||
|
knowledgeSegments);
|
||||||
|
|
||||||
|
// 4.2 构建 Prompt,并进行调用
|
||||||
// 3.2 召回段落
|
Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO);
|
||||||
List<AiKnowledgeSegmentDO> segmentList = recallSegment(sendReqVO.getContent(), conversation.getKnowledgeId());
|
|
||||||
|
|
||||||
// 3.3 构建 Prompt,并进行调用
|
|
||||||
Prompt prompt = buildPrompt(conversation, historyMessages, segmentList, model, sendReqVO);
|
|
||||||
Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
|
Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
|
||||||
|
|
||||||
// 3.4 流式返回
|
// 4.3 流式返回
|
||||||
// TODO 注意:Schedulers.immediate() 目的是,避免默认 Schedulers.parallel() 并发消费 chunk 导致 SSE 响应前端会乱序问题
|
|
||||||
StringBuffer contentBuffer = new StringBuffer();
|
StringBuffer contentBuffer = new StringBuffer();
|
||||||
return streamResponse.map(chunk -> {
|
return streamResponse.map(chunk -> {
|
||||||
String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null;
|
// 处理知识库的返回,只有首次才有
|
||||||
|
List<AiChatMessageRespVO.KnowledgeSegment> segments = null;
|
||||||
|
if (StrUtil.isEmpty(contentBuffer)) {
|
||||||
|
segments = BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class,
|
||||||
|
segment -> TenantUtils.executeIgnore(() -> {
|
||||||
|
AiKnowledgeDocumentDO document = knowledgeDocumentService
|
||||||
|
.getKnowledgeDocument(segment.getDocumentId());
|
||||||
|
segment.setDocumentName(document != null ? document.getName() : null);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// 响应结果
|
||||||
|
String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;
|
||||||
newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
|
newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
|
||||||
contentBuffer.append(newContent);
|
contentBuffer.append(newContent);
|
||||||
// 响应结果
|
return success(new AiChatMessageSendRespVO()
|
||||||
return success(new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
|
.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).setSegments(segments)));
|
||||||
}).doOnComplete(() -> {
|
}).doOnComplete(() -> {
|
||||||
// 忽略租户,因为 Flux 异步无法透传租户
|
// 忽略租户,因为 Flux 异步无法透传租户
|
||||||
TenantUtils.executeIgnore(() ->
|
TenantUtils.executeIgnore(() -> chatMessageMapper.updateById(
|
||||||
chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId))
|
new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString())));
|
||||||
.setContent(contentBuffer.toString())));
|
|
||||||
}).doOnError(throwable -> {
|
}).doOnError(throwable -> {
|
||||||
log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable);
|
log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable);
|
||||||
// 忽略租户,因为 Flux 异步无法透传租户
|
// 忽略租户,因为 Flux 异步无法透传租户
|
||||||
TenantUtils.executeIgnore(() ->
|
TenantUtils.executeIgnore(() -> chatMessageMapper.updateById(
|
||||||
chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage())));
|
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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<AiKnowledgeSegmentDO> recallSegment(String content, Long knowledgeId) {
|
private List<AiKnowledgeSegmentSearchRespBO> recallKnowledgeSegment(String content,
|
||||||
if (Objects.isNull(knowledgeId)) {
|
AiChatConversationDO conversation) {
|
||||||
|
// 1. 查询聊天角色
|
||||||
|
if (conversation == null || conversation.getRoleId() == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
return knowledgeSegmentService.similaritySearch(new AiKnowledgeSegmentSearchReqVO().setKnowledgeId(knowledgeId).setContent(content));
|
AiChatRoleDO role = chatRoleService.getChatRole(conversation.getRoleId());
|
||||||
|
if (role == null || CollUtil.isEmpty(role.getKnowledgeIds())) {
|
||||||
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages,List<AiKnowledgeSegmentDO> segmentList,
|
// 2. 遍历找回
|
||||||
AiChatModelDO model, AiChatMessageSendReqVO sendReqVO) {
|
List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = new ArrayList<>();
|
||||||
// 1. 构建 Prompt Message 列表
|
for (Long knowledgeId : role.getKnowledgeIds()) {
|
||||||
|
knowledgeSegments.addAll(knowledgeSegmentService.searchKnowledgeSegment(new AiKnowledgeSegmentSearchReqBO()
|
||||||
|
.setKnowledgeId(knowledgeId).setContent(content)));
|
||||||
|
}
|
||||||
|
return knowledgeSegments;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages,
|
||||||
|
List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments,
|
||||||
|
AiModelDO model, AiChatMessageSendReqVO sendReqVO) {
|
||||||
List<Message> chatMessages = new ArrayList<>();
|
List<Message> chatMessages = new ArrayList<>();
|
||||||
|
// 1.1 System Context 角色设定
|
||||||
// 1.1 召回内容消息构建
|
|
||||||
if (CollUtil.isNotEmpty(segmentList)) {
|
|
||||||
PromptTemplate promptTemplate = new PromptTemplate(AiChatRoleEnum.AI_KNOWLEDGE_ROLE.getSystemMessage());
|
|
||||||
StringBuilder infoBuilder = StrUtil.builder();
|
|
||||||
segmentList.forEach(segment -> infoBuilder.append(System.lineSeparator()).append(segment.getContent()));
|
|
||||||
Message message = promptTemplate.createMessage(Map.of("info", infoBuilder.toString()));
|
|
||||||
chatMessages.add(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1.2 system context 角色设定
|
|
||||||
if (StrUtil.isNotBlank(conversation.getSystemMessage())) {
|
if (StrUtil.isNotBlank(conversation.getSystemMessage())) {
|
||||||
chatMessages.add(new SystemMessage(conversation.getSystemMessage()));
|
chatMessages.add(new SystemMessage(conversation.getSystemMessage()));
|
||||||
}
|
}
|
||||||
// 1.3 history message 历史消息
|
|
||||||
|
// 1.2 历史 history message 历史消息
|
||||||
List<AiChatMessageDO> contextMessages = filterContextMessages(messages, conversation, sendReqVO);
|
List<AiChatMessageDO> contextMessages = filterContextMessages(messages, conversation, sendReqVO);
|
||||||
contextMessages.forEach(message -> chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent())));
|
contextMessages
|
||||||
// 1.4 user message 新发送消息
|
.forEach(message -> chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent())));
|
||||||
|
|
||||||
|
// 1.3 当前 user message 新发送消息
|
||||||
chatMessages.add(new UserMessage(sendReqVO.getContent()));
|
chatMessages.add(new UserMessage(sendReqVO.getContent()));
|
||||||
|
|
||||||
// 2. 构建 ChatOptions 对象
|
// 1.4 知识库,通过 UserMessage 实现
|
||||||
|
if (CollUtil.isNotEmpty(knowledgeSegments)) {
|
||||||
|
String reference = knowledgeSegments.stream()
|
||||||
|
.map(segment -> "<Reference>" + segment.getContent() + "</Reference>")
|
||||||
|
.collect(Collectors.joining("\n\n"));
|
||||||
|
chatMessages.add(new UserMessage(String.format(KNOWLEDGE_USER_MESSAGE_TEMPLATE, reference)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.1 查询 tool 工具
|
||||||
|
Set<String> toolNames = null;
|
||||||
|
if (conversation.getRoleId() != null) {
|
||||||
|
AiChatRoleDO chatRole = chatRoleService.getChatRole(conversation.getRoleId());
|
||||||
|
if (chatRole != null && CollUtil.isNotEmpty(chatRole.getToolIds())) {
|
||||||
|
toolNames = convertSet(toolService.getToolList(chatRole.getToolIds()), AiToolDO::getName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2.2 构建 ChatOptions 对象
|
||||||
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
|
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
|
||||||
ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(),
|
ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(),
|
||||||
conversation.getTemperature(), conversation.getMaxTokens());
|
conversation.getTemperature(), conversation.getMaxTokens(), toolNames);
|
||||||
return new Prompt(chatMessages, chatOptions);
|
return new Prompt(chatMessages, chatOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +283,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
AiChatMessageDO userMessage = CollUtil.get(messages, i - 1);
|
AiChatMessageDO userMessage = CollUtil.get(messages, i - 1);
|
||||||
if (userMessage == null || ObjUtil.notEqual(assistantMessage.getReplyId(), userMessage.getId())
|
if (userMessage == null
|
||||||
|
|| ObjUtil.notEqual(assistantMessage.getReplyId(), userMessage.getId())
|
||||||
|| StrUtil.isEmpty(assistantMessage.getContent())) {
|
|| StrUtil.isEmpty(assistantMessage.getContent())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -233,11 +301,13 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private AiChatMessageDO createChatMessage(Long conversationId, Long replyId,
|
private AiChatMessageDO createChatMessage(Long conversationId, Long replyId,
|
||||||
AiChatModelDO model, Long userId, Long roleId,
|
AiModelDO model, Long userId, Long roleId,
|
||||||
MessageType messageType, String content, Boolean useContext) {
|
MessageType messageType, String content, Boolean useContext,
|
||||||
|
List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments) {
|
||||||
AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId)
|
AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId)
|
||||||
.setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId)
|
.setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId)
|
||||||
.setType(messageType.getValue()).setContent(content).setUseContext(useContext);
|
.setType(messageType.getValue()).setContent(content).setUseContext(useContext)
|
||||||
|
.setSegmentIds(convertList(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getId));
|
||||||
message.setCreateTime(LocalDateTime.now());
|
message.setCreateTime(LocalDateTime.now());
|
||||||
chatMessageMapper.insert(message);
|
chatMessageMapper.insert(message);
|
||||||
return message;
|
return message;
|
||||||
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.service.image;
|
|||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
import cn.hutool.core.codec.Base64;
|
import cn.hutool.core.codec.Base64;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.ObjUtil;
|
import cn.hutool.core.util.ObjUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
@ -12,15 +13,19 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
|||||||
import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
|
import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
|
||||||
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.module.ai.controller.admin.image.vo.*;
|
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePageReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePublicPageReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageUpdateReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper;
|
import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper;
|
||||||
import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;
|
import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
|
import cn.iocoder.yudao.module.ai.service.model.AiModelService;
|
||||||
import cn.iocoder.yudao.module.infra.api.file.FileApi;
|
import cn.iocoder.yudao.module.infra.api.file.FileApi;
|
||||||
import com.alibaba.cloud.ai.tongyi.image.TongYiImagesOptions;
|
import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.ai.image.ImageModel;
|
import org.springframework.ai.image.ImageModel;
|
||||||
@ -54,15 +59,15 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class AiImageServiceImpl implements AiImageService {
|
public class AiImageServiceImpl implements AiImageService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AiModelService modelService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiImageMapper imageMapper;
|
private AiImageMapper imageMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private FileApi fileApi;
|
private FileApi fileApi;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private AiApiKeyService apiKeyService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<AiImageDO> getImagePageMy(Long userId, AiImagePageReqVO pageReqVO) {
|
public PageResult<AiImageDO> getImagePageMy(Long userId, AiImagePageReqVO pageReqVO) {
|
||||||
return imageMapper.selectPageMy(userId, pageReqVO);
|
return imageMapper.selectPageMy(userId, pageReqVO);
|
||||||
@ -88,23 +93,31 @@ public class AiImageServiceImpl implements AiImageService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long drawImage(Long userId, AiImageDrawReqVO drawReqVO) {
|
public Long drawImage(Long userId, AiImageDrawReqVO drawReqVO) {
|
||||||
// 1. 保存数据库
|
// 1. 校验模型
|
||||||
AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false)
|
AiModelDO model = modelService.validateModel(drawReqVO.getModelId());
|
||||||
.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());
|
|
||||||
|
// 2. 保存数据库
|
||||||
|
AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId)
|
||||||
|
.setPlatform(model.getPlatform()).setModelId(model.getId()).setModel(model.getModel())
|
||||||
|
.setPublicStatus(false).setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());
|
||||||
imageMapper.insert(image);
|
imageMapper.insert(image);
|
||||||
// 2. 异步绘制,后续前端通过返回的 id 进行轮询结果
|
|
||||||
getSelf().executeDrawImage(image, drawReqVO);
|
// 3. 异步绘制,后续前端通过返回的 id 进行轮询结果
|
||||||
|
getSelf().executeDrawImage(image, drawReqVO, model);
|
||||||
return image.getId();
|
return image.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Async
|
@Async
|
||||||
public void executeDrawImage(AiImageDO image, AiImageDrawReqVO req) {
|
public void executeDrawImage(AiImageDO image, AiImageDrawReqVO reqVO, AiModelDO model) {
|
||||||
try {
|
try {
|
||||||
// 1.1 构建请求
|
// 1.1 构建请求
|
||||||
ImageOptions request = buildImageOptions(req);
|
ImageOptions request = buildImageOptions(reqVO, model);
|
||||||
// 1.2 执行请求
|
// 1.2 执行请求
|
||||||
ImageModel imageModel = apiKeyService.getImageModel(AiPlatformEnum.validatePlatform(req.getPlatform()));
|
ImageModel imageModel = modelService.getImageModel(model.getId());
|
||||||
ImageResponse response = imageModel.call(new ImagePrompt(req.getPrompt(), request));
|
ImageResponse response = imageModel.call(new ImagePrompt(reqVO.getPrompt(), request));
|
||||||
|
if (response.getResult() == null) {
|
||||||
|
throw new IllegalArgumentException("生成结果为空");
|
||||||
|
}
|
||||||
|
|
||||||
// 2. 上传到文件服务
|
// 2. 上传到文件服务
|
||||||
String b64Json = response.getResult().getOutput().getB64Json();
|
String b64Json = response.getResult().getOutput().getB64Json();
|
||||||
@ -116,49 +129,49 @@ public class AiImageServiceImpl implements AiImageService {
|
|||||||
imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(AiImageStatusEnum.SUCCESS.getStatus())
|
imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(AiImageStatusEnum.SUCCESS.getStatus())
|
||||||
.setPicUrl(filePath).setFinishTime(LocalDateTime.now()));
|
.setPicUrl(filePath).setFinishTime(LocalDateTime.now()));
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.error("[doDall][image({}) 生成异常]", image, ex);
|
log.error("[executeDrawImage][image({}) 生成异常]", image, ex);
|
||||||
imageMapper.updateById(new AiImageDO().setId(image.getId())
|
imageMapper.updateById(new AiImageDO().setId(image.getId())
|
||||||
.setStatus(AiImageStatusEnum.FAIL.getStatus())
|
.setStatus(AiImageStatusEnum.FAIL.getStatus())
|
||||||
.setErrorMessage(ex.getMessage()).setFinishTime(LocalDateTime.now()));
|
.setErrorMessage(ex.getMessage()).setFinishTime(LocalDateTime.now()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ImageOptions buildImageOptions(AiImageDrawReqVO draw) {
|
private static ImageOptions buildImageOptions(AiImageDrawReqVO draw, AiModelDO model) {
|
||||||
if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) {
|
if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) {
|
||||||
// https://platform.openai.com/docs/api-reference/images/create
|
// https://platform.openai.com/docs/api-reference/images/create
|
||||||
return OpenAiImageOptions.builder().withModel(draw.getModel())
|
return OpenAiImageOptions.builder().withModel(model.getModel())
|
||||||
.withHeight(draw.getHeight()).withWidth(draw.getWidth())
|
.withHeight(draw.getHeight()).withWidth(draw.getWidth())
|
||||||
.withStyle(MapUtil.getStr(draw.getOptions(), "style")) // 风格
|
.withStyle(MapUtil.getStr(draw.getOptions(), "style")) // 风格
|
||||||
.withResponseFormat("b64_json")
|
.withResponseFormat("b64_json")
|
||||||
.build();
|
.build();
|
||||||
} else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) {
|
} else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) {
|
||||||
// https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage
|
// https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage
|
||||||
// https://platform.stability.ai/docs/api-reference#tag/Text-to-Image/operation/textToImage
|
// https://platform.stability.ai/docs/api-reference#tag/Text-to-Image/operation/textToImage
|
||||||
return StabilityAiImageOptions.builder().withModel(draw.getModel())
|
return StabilityAiImageOptions.builder().model(model.getModel())
|
||||||
.withHeight(draw.getHeight()).withWidth(draw.getWidth())
|
.height(draw.getHeight()).width(draw.getWidth())
|
||||||
.withSeed(Long.valueOf(draw.getOptions().get("seed")))
|
.seed(Long.valueOf(draw.getOptions().get("seed")))
|
||||||
.withCfgScale(Float.valueOf(draw.getOptions().get("scale")))
|
.cfgScale(Float.valueOf(draw.getOptions().get("scale")))
|
||||||
.withSteps(Integer.valueOf(draw.getOptions().get("steps")))
|
.steps(Integer.valueOf(draw.getOptions().get("steps")))
|
||||||
.withSampler(String.valueOf(draw.getOptions().get("sampler")))
|
.sampler(String.valueOf(draw.getOptions().get("sampler")))
|
||||||
.withStylePreset(String.valueOf(draw.getOptions().get("stylePreset")))
|
.stylePreset(String.valueOf(draw.getOptions().get("stylePreset")))
|
||||||
.withClipGuidancePreset(String.valueOf(draw.getOptions().get("clipGuidancePreset")))
|
.clipGuidancePreset(String.valueOf(draw.getOptions().get("clipGuidancePreset")))
|
||||||
.build();
|
.build();
|
||||||
} else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) {
|
} else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) {
|
||||||
return TongYiImagesOptions.builder()
|
return DashScopeImageOptions.builder()
|
||||||
.withModel(draw.getModel()).withN(1)
|
.withModel(model.getModel()).withN(1)
|
||||||
.withHeight(draw.getHeight()).withWidth(draw.getWidth())
|
.withHeight(draw.getHeight()).withWidth(draw.getWidth())
|
||||||
.build();
|
.build();
|
||||||
} else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) {
|
} else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) {
|
||||||
return QianFanImageOptions.builder()
|
return QianFanImageOptions.builder()
|
||||||
.withModel(draw.getModel()).withN(1)
|
.model(model.getModel()).N(1)
|
||||||
.withHeight(draw.getHeight()).withWidth(draw.getWidth())
|
.height(draw.getHeight()).width(draw.getWidth())
|
||||||
.build();
|
.build();
|
||||||
} else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) {
|
} else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) {
|
||||||
return ZhiPuAiImageOptions.builder()
|
return ZhiPuAiImageOptions.builder()
|
||||||
.withModel(draw.getModel())
|
.model(model.getModel())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("不支持的 AI 平台:" + draw.getPlatform());
|
throw new IllegalArgumentException("不支持的 AI 平台:" + model.getPlatform());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -205,52 +218,56 @@ public class AiImageServiceImpl implements AiImageService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO reqVO) {
|
public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO drawReqVO) {
|
||||||
MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi();
|
// 1. 校验模型
|
||||||
// 1. 保存数据库
|
AiModelDO model = modelService.validateModel(drawReqVO.getModelId());
|
||||||
AiImageDO image = BeanUtils.toBean(reqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false)
|
Assert.equals(model.getPlatform(), AiPlatformEnum.MIDJOURNEY.getPlatform(), "平台不匹配");
|
||||||
|
MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(model.getId());
|
||||||
|
|
||||||
|
// 2. 保存数据库
|
||||||
|
AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false)
|
||||||
.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus())
|
.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus())
|
||||||
.setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform());
|
.setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()).setModelId(model.getId()).setModel(model.getName());
|
||||||
imageMapper.insert(image);
|
imageMapper.insert(image);
|
||||||
|
|
||||||
// 2. 调用 Midjourney Proxy 提交任务
|
// 3. 调用 Midjourney Proxy 提交任务
|
||||||
List<String> base64Array = StrUtil.isBlank(reqVO.getReferImageUrl()) ? null :
|
List<String> base64Array = StrUtil.isBlank(drawReqVO.getReferImageUrl()) ? null :
|
||||||
Collections.singletonList("data:image/jpeg;base64,".concat(Base64.encode(HttpUtil.downloadBytes(reqVO.getReferImageUrl()))));
|
Collections.singletonList("data:image/jpeg;base64,".concat(Base64.encode(HttpUtil.downloadBytes(drawReqVO.getReferImageUrl()))));
|
||||||
MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest(
|
MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest(
|
||||||
base64Array, reqVO.getPrompt(),null,
|
base64Array, drawReqVO.getPrompt(),null,
|
||||||
MidjourneyApi.ImagineRequest.buildState(reqVO.getWidth(),
|
MidjourneyApi.ImagineRequest.buildState(drawReqVO.getWidth(),
|
||||||
reqVO.getHeight(), reqVO.getVersion(), reqVO.getModel()));
|
drawReqVO.getHeight(), drawReqVO.getVersion(), model.getModel()));
|
||||||
MidjourneyApi.SubmitResponse imagineResponse = midjourneyApi.imagine(imagineRequest);
|
MidjourneyApi.SubmitResponse imagineResponse = midjourneyApi.imagine(imagineRequest);
|
||||||
|
|
||||||
// 3. 情况一【失败】:抛出业务异常
|
// 4.1 情况一【失败】:抛出业务异常
|
||||||
if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(imagineResponse.code())) {
|
if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(imagineResponse.code())) {
|
||||||
String description = imagineResponse.description().contains("quota_not_enough") ?
|
String description = imagineResponse.description().contains("quota_not_enough") ?
|
||||||
"账户余额不足" : imagineResponse.description();
|
"账户余额不足" : imagineResponse.description();
|
||||||
throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description);
|
throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 情况二【成功】:更新 taskId 和参数
|
// 4.2 情况二【成功】:更新 taskId 和参数
|
||||||
imageMapper.updateById(new AiImageDO().setId(image.getId())
|
imageMapper.updateById(new AiImageDO().setId(image.getId())
|
||||||
.setTaskId(imagineResponse.result()).setOptions(BeanUtil.beanToMap(reqVO)));
|
.setTaskId(imagineResponse.result()).setOptions(BeanUtil.beanToMap(drawReqVO)));
|
||||||
return image.getId();
|
return image.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer midjourneySync() {
|
public Integer midjourneySync() {
|
||||||
MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi();
|
|
||||||
// 1.1 获取 Midjourney 平台,状态在 “进行中” 的 image
|
// 1.1 获取 Midjourney 平台,状态在 “进行中” 的 image
|
||||||
List<AiImageDO> imageList = imageMapper.selectListByStatusAndPlatform(
|
List<AiImageDO> images = imageMapper.selectListByStatusAndPlatform(
|
||||||
AiImageStatusEnum.IN_PROGRESS.getStatus(), AiPlatformEnum.MIDJOURNEY.getPlatform());
|
AiImageStatusEnum.IN_PROGRESS.getStatus(), AiPlatformEnum.MIDJOURNEY.getPlatform());
|
||||||
if (CollUtil.isEmpty(imageList)) {
|
if (CollUtil.isEmpty(images)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// 1.2 调用 Midjourney Proxy 获取任务进展
|
// 1.2 调用 Midjourney Proxy 获取任务进展
|
||||||
List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(convertSet(imageList, AiImageDO::getTaskId));
|
MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(images.get(0).getModelId());
|
||||||
|
List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(convertSet(images, AiImageDO::getTaskId));
|
||||||
Map<String, MidjourneyApi.Notify> taskMap = convertMap(taskList, MidjourneyApi.Notify::id);
|
Map<String, MidjourneyApi.Notify> taskMap = convertMap(taskList, MidjourneyApi.Notify::id);
|
||||||
|
|
||||||
// 2. 逐个处理,更新进展
|
// 2. 逐个处理,更新进展
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (AiImageDO image : imageList) {
|
for (AiImageDO image : images) {
|
||||||
MidjourneyApi.Notify notify = taskMap.get(image.getTaskId());
|
MidjourneyApi.Notify notify = taskMap.get(image.getTaskId());
|
||||||
if (notify == null) {
|
if (notify == null) {
|
||||||
log.error("[midjourneySync][image({}) 查询不到进展]", image);
|
log.error("[midjourneySync][image({}) 查询不到进展]", image);
|
||||||
@ -308,12 +325,12 @@ public class AiImageServiceImpl implements AiImageService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO) {
|
public Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO) {
|
||||||
MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi();
|
|
||||||
// 1.1 检查 image
|
// 1.1 检查 image
|
||||||
AiImageDO image = validateImageExists(reqVO.getId());
|
AiImageDO image = validateImageExists(reqVO.getId());
|
||||||
if (ObjUtil.notEqual(userId, image.getUserId())) {
|
if (ObjUtil.notEqual(userId, image.getUserId())) {
|
||||||
throw exception(IMAGE_NOT_EXISTS);
|
throw exception(IMAGE_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(image.getModelId());
|
||||||
// 1.2 检查 customId
|
// 1.2 检查 customId
|
||||||
MidjourneyApi.Button button = CollUtil.findOne(image.getButtons(),
|
MidjourneyApi.Button button = CollUtil.findOne(image.getButtons(),
|
||||||
buttonX -> buttonX.customId().equals(reqVO.getCustomId()));
|
buttonX -> buttonX.customId().equals(reqVO.getCustomId()));
|
||||||
|
@ -1,26 +1,41 @@
|
|||||||
package cn.iocoder.yudao.module.ai.service.knowledge;
|
package cn.iocoder.yudao.module.ai.service.knowledge;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI 知识库-文档 Service 接口
|
* AI 知识库文档 Service 接口
|
||||||
*
|
*
|
||||||
* @author xiaoxin
|
* @author xiaoxin
|
||||||
*/
|
*/
|
||||||
public interface AiKnowledgeDocumentService {
|
public interface AiKnowledgeDocumentService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建文档
|
* 创建文档(单个)
|
||||||
*
|
*
|
||||||
* @param createReqVO 文档创建 Request VO
|
* @param createReqVO 文档创建 Request VO
|
||||||
* @return 文档编号
|
* @return 文档编号
|
||||||
*/
|
*/
|
||||||
Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO);
|
Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建文档(多个)
|
||||||
|
*
|
||||||
|
* @param createListReqVO 批量创建 Request VO
|
||||||
|
* @return 文档编号列表
|
||||||
|
*/
|
||||||
|
List<Long> createKnowledgeDocumentList(AiKnowledgeDocumentCreateListReqVO createListReqVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取文档分页
|
* 获取文档分页
|
||||||
@ -30,10 +45,74 @@ public interface AiKnowledgeDocumentService {
|
|||||||
*/
|
*/
|
||||||
PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO);
|
PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文档详情
|
||||||
|
*
|
||||||
|
* @param id 文档编号
|
||||||
|
* @return 文档详情
|
||||||
|
*/
|
||||||
|
AiKnowledgeDocumentDO getKnowledgeDocument(Long id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新文档
|
* 更新文档
|
||||||
*
|
*
|
||||||
* @param reqVO 更新信息
|
* @param reqVO 更新信息
|
||||||
*/
|
*/
|
||||||
void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO);
|
void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新文档状态
|
||||||
|
*
|
||||||
|
* @param reqVO 更新状态信息
|
||||||
|
*/
|
||||||
|
void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新文档检索次数(增加 +1)
|
||||||
|
*
|
||||||
|
* @param ids 文档编号列表
|
||||||
|
*/
|
||||||
|
void updateKnowledgeDocumentRetrievalCountIncr(Collection<Long> ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文档
|
||||||
|
*
|
||||||
|
* @param id 文档编号
|
||||||
|
*/
|
||||||
|
void deleteKnowledgeDocument(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验文档是否存在
|
||||||
|
*
|
||||||
|
* @param id 文档编号
|
||||||
|
* @return 文档信息
|
||||||
|
*/
|
||||||
|
AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取 URL 内容
|
||||||
|
*
|
||||||
|
* @param url URL
|
||||||
|
* @return 内容
|
||||||
|
*/
|
||||||
|
String readUrl(String url);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文档列表
|
||||||
|
*
|
||||||
|
* @param ids 文档编号列表
|
||||||
|
* @return 文档列表
|
||||||
|
*/
|
||||||
|
List<AiKnowledgeDocumentDO> getKnowledgeDocumentList(Collection<Long> ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文档 Map
|
||||||
|
*
|
||||||
|
* @param ids 文档编号列表
|
||||||
|
* @return 文档 Map
|
||||||
|
*/
|
||||||
|
default Map<Long, AiKnowledgeDocumentDO> getKnowledgeDocumentMap(Collection<Long> ids) {
|
||||||
|
return convertMap(getKnowledgeDocumentList(ids), AiKnowledgeDocumentDO::getId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,37 @@
|
|||||||
package cn.iocoder.yudao.module.ai.service.knowledge;
|
package cn.iocoder.yudao.module.ai.service.knowledge;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.http.HttpUtil;
|
import cn.hutool.http.HttpUtil;
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper;
|
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper;
|
||||||
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper;
|
|
||||||
import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum;
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.ai.document.Document;
|
import org.springframework.ai.document.Document;
|
||||||
import org.springframework.ai.reader.tika.TikaDocumentReader;
|
import org.springframework.ai.reader.tika.TikaDocumentReader;
|
||||||
import org.springframework.ai.tokenizer.TokenCountEstimator;
|
import org.springframework.ai.tokenizer.TokenCountEstimator;
|
||||||
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.ai.vectorstore.VectorStore;
|
|
||||||
import org.springframework.core.io.ByteArrayResource;
|
import org.springframework.core.io.ByteArrayResource;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_DOCUMENT_NOT_EXISTS;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI 知识库文档 Service 实现类
|
* AI 知识库文档 Service 实现类
|
||||||
@ -40,91 +43,172 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_DOCU
|
|||||||
public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentService {
|
public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentService {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiKnowledgeDocumentMapper documentMapper;
|
private AiKnowledgeDocumentMapper knowledgeDocumentMapper;
|
||||||
@Resource
|
|
||||||
private AiKnowledgeSegmentMapper segmentMapper;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private TokenCountEstimator tokenCountEstimator;
|
private TokenCountEstimator tokenCountEstimator;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
|
private AiKnowledgeSegmentService knowledgeSegmentService;
|
||||||
|
@Resource
|
||||||
|
@Lazy // 延迟加载,避免循环依赖
|
||||||
private AiKnowledgeService knowledgeService;
|
private AiKnowledgeService knowledgeService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) {
|
public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) {
|
||||||
// 0. 校验并获取向量存储实例
|
// 1. 校验参数
|
||||||
VectorStore vectorStore = knowledgeService.getVectorStoreById(createReqVO.getKnowledgeId());
|
knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId());
|
||||||
|
|
||||||
// 1.1 下载文档
|
// 2. 下载文档
|
||||||
TikaDocumentReader loader = new TikaDocumentReader(downloadFile(createReqVO.getUrl()));
|
String content = readUrl(createReqVO.getUrl());
|
||||||
List<Document> documents = loader.get();
|
|
||||||
Document document = CollUtil.getFirst(documents);
|
// 3. 文档记录入库
|
||||||
// 1.2 文档记录入库
|
|
||||||
String content = document.getContent();
|
|
||||||
AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class)
|
AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class)
|
||||||
.setTokens(tokenCountEstimator.estimate(content)).setWordCount(content.length())
|
.setContent(content).setContentLength(content.length()).setTokens(tokenCountEstimator.estimate(content))
|
||||||
.setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus());
|
.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||||
documentMapper.insert(documentDO);
|
knowledgeDocumentMapper.insert(documentDO);
|
||||||
Long documentId = documentDO.getId();
|
|
||||||
if (CollUtil.isEmpty(documents)) {
|
// 4. 文档切片入库(异步)
|
||||||
return documentId;
|
knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(documentDO.getId(), content);
|
||||||
|
return documentDO.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2 构造文本分段器
|
@Override
|
||||||
TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(createReqVO.getDefaultSegmentTokens(), createReqVO.getMinSegmentWordCount(), createReqVO.getMinChunkLengthToEmbed(),
|
public List<Long> createKnowledgeDocumentList(AiKnowledgeDocumentCreateListReqVO createListReqVO) {
|
||||||
createReqVO.getMaxNumSegments(), createReqVO.getKeepSeparator());
|
// 1. 校验参数
|
||||||
// 2.1 文档分段
|
knowledgeService.validateKnowledgeExists(createListReqVO.getKnowledgeId());
|
||||||
List<Document> segments = tokenTextSplitter.apply(documents);
|
|
||||||
// 2.2 分段内容入库
|
|
||||||
List<AiKnowledgeSegmentDO> segmentDOList = CollectionUtils.convertList(segments,
|
|
||||||
segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId)
|
|
||||||
.setKnowledgeId(createReqVO.getKnowledgeId()).setVectorId(segment.getId())
|
|
||||||
.setTokens(tokenCountEstimator.estimate(segment.getContent())).setWordCount(segment.getContent().length())
|
|
||||||
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
|
||||||
segmentMapper.insertBatch(segmentDOList);
|
|
||||||
|
|
||||||
// 3. 向量化并存储
|
// 2. 下载文档
|
||||||
segments.forEach(segment -> segment.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, createReqVO.getKnowledgeId()));
|
List<String> contents = convertList(createListReqVO.getList(), document -> readUrl(document.getUrl()));
|
||||||
vectorStore.add(segments);
|
|
||||||
return documentId;
|
// 3. 文档记录入库
|
||||||
|
List<AiKnowledgeDocumentDO> documentDOs = new ArrayList<>(createListReqVO.getList().size());
|
||||||
|
for (int i = 0; i < createListReqVO.getList().size(); i++) {
|
||||||
|
AiKnowledgeDocumentCreateListReqVO.Document documentVO = createListReqVO.getList().get(i);
|
||||||
|
String content = contents.get(i);
|
||||||
|
documentDOs.add(BeanUtils.toBean(documentVO, AiKnowledgeDocumentDO.class)
|
||||||
|
.setKnowledgeId(createListReqVO.getKnowledgeId())
|
||||||
|
.setContent(content).setContentLength(content.length())
|
||||||
|
.setTokens(tokenCountEstimator.estimate(content))
|
||||||
|
.setSegmentMaxTokens(createListReqVO.getSegmentMaxTokens())
|
||||||
|
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||||
|
}
|
||||||
|
knowledgeDocumentMapper.insertBatch(documentDOs);
|
||||||
|
|
||||||
|
// 4. 批量创建文档切片(异步)
|
||||||
|
documentDOs.forEach(documentDO -> knowledgeSegmentService
|
||||||
|
.createKnowledgeSegmentBySplitContentAsync(documentDO.getId(), documentDO.getContent()));
|
||||||
|
return convertList(documentDOs, AiKnowledgeDocumentDO::getId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO) {
|
public PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO) {
|
||||||
return documentMapper.selectPage(pageReqVO);
|
return knowledgeDocumentMapper.selectPage(pageReqVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AiKnowledgeDocumentDO getKnowledgeDocument(Long id) {
|
||||||
|
return knowledgeDocumentMapper.selectById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO) {
|
public void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO) {
|
||||||
// 1. 校验文档是否存在
|
// 1. 校验文档是否存在
|
||||||
validateKnowledgeDocumentExists(reqVO.getId());
|
AiKnowledgeDocumentDO oldDocument = validateKnowledgeDocumentExists(reqVO.getId());
|
||||||
|
|
||||||
// 2. 更新文档
|
// 2. 更新文档
|
||||||
AiKnowledgeDocumentDO document = BeanUtils.toBean(reqVO, AiKnowledgeDocumentDO.class);
|
AiKnowledgeDocumentDO document = BeanUtils.toBean(reqVO, AiKnowledgeDocumentDO.class);
|
||||||
documentMapper.updateById(document);
|
knowledgeDocumentMapper.updateById(document);
|
||||||
|
|
||||||
|
// 3. 如果处于开启状态,并且最大 tokens 发生变化,则 segment 需要重新索引
|
||||||
|
if (CommonStatusEnum.isEnable(oldDocument.getStatus())
|
||||||
|
&& reqVO.getSegmentMaxTokens() != null
|
||||||
|
&& ObjUtil.notEqual(reqVO.getSegmentMaxTokens(), oldDocument.getSegmentMaxTokens())) {
|
||||||
|
// 删除旧的文档切片
|
||||||
|
knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(reqVO.getId());
|
||||||
|
// 重新创建文档切片
|
||||||
|
knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(reqVO.getId(), oldDocument.getContent());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* 校验文档是否存在
|
public void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO) {
|
||||||
*
|
// 1. 校验存在
|
||||||
* @param id 文档编号
|
AiKnowledgeDocumentDO document = validateKnowledgeDocumentExists(reqVO.getId());
|
||||||
* @return 文档信息
|
|
||||||
*/
|
// 2. 更新状态
|
||||||
private AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id) {
|
knowledgeDocumentMapper.updateById(new AiKnowledgeDocumentDO()
|
||||||
AiKnowledgeDocumentDO knowledgeDocument = documentMapper.selectById(id);
|
.setId(reqVO.getId()).setStatus(reqVO.getStatus()));
|
||||||
|
|
||||||
|
// 3. 处理文档切片
|
||||||
|
if (CommonStatusEnum.isEnable(reqVO.getStatus())) {
|
||||||
|
knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(reqVO.getId(), document.getContent());
|
||||||
|
} else {
|
||||||
|
knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(reqVO.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void deleteKnowledgeDocument(Long id) {
|
||||||
|
// 1. 校验存在
|
||||||
|
validateKnowledgeDocumentExists(id);
|
||||||
|
|
||||||
|
// 2. 删除
|
||||||
|
knowledgeDocumentMapper.deleteById(id);
|
||||||
|
|
||||||
|
// 3. 删除对应的段落
|
||||||
|
knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateKnowledgeDocumentRetrievalCountIncr(Collection<Long> ids) {
|
||||||
|
if (CollUtil.isEmpty(ids)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
knowledgeDocumentMapper.updateRetrievalCountIncr(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id) {
|
||||||
|
AiKnowledgeDocumentDO knowledgeDocument = knowledgeDocumentMapper.selectById(id);
|
||||||
if (knowledgeDocument == null) {
|
if (knowledgeDocument == null) {
|
||||||
throw exception(KNOWLEDGE_DOCUMENT_NOT_EXISTS);
|
throw exception(KNOWLEDGE_DOCUMENT_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
return knowledgeDocument;
|
return knowledgeDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
private org.springframework.core.io.Resource downloadFile(String url) {
|
@Override
|
||||||
|
public String readUrl(String url) {
|
||||||
|
// 下载文件
|
||||||
|
ByteArrayResource resource;
|
||||||
try {
|
try {
|
||||||
byte[] bytes = HttpUtil.downloadBytes(url);
|
byte[] bytes = HttpUtil.downloadBytes(url);
|
||||||
return new ByteArrayResource(bytes);
|
if (bytes.length == 0) {
|
||||||
} catch (Exception e) {
|
throw exception(KNOWLEDGE_DOCUMENT_FILE_EMPTY);
|
||||||
log.error("[downloadFile][url({}) 下载失败]", url, e);
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
|
resource = new ByteArrayResource(bytes);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("[readUrl][url({}) 读取失败]", url, e);
|
||||||
|
throw exception(KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件
|
||||||
|
TikaDocumentReader loader = new TikaDocumentReader(resource);
|
||||||
|
List<Document> documents = loader.get();
|
||||||
|
Document document = CollUtil.getFirst(documents);
|
||||||
|
if (document == null || StrUtil.isEmpty(document.getText())) {
|
||||||
|
throw exception(KNOWLEDGE_DOCUMENT_FILE_READ_FAIL);
|
||||||
|
}
|
||||||
|
return document.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AiKnowledgeDocumentDO> getKnowledgeDocumentList(Collection<Long> ids) {
|
||||||
|
if (CollUtil.isEmpty(ids)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return knowledgeDocumentMapper.selectBatchIds(ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,19 @@ package cn.iocoder.yudao.module.ai.service.knowledge;
|
|||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSaveReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI 知识库段落 Service 接口
|
* AI 知识库段落 Service 接口
|
||||||
@ -16,6 +23,32 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public interface AiKnowledgeSegmentService {
|
public interface AiKnowledgeSegmentService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取知识库段落详情
|
||||||
|
*
|
||||||
|
* @param id 段落编号
|
||||||
|
* @return 段落详情
|
||||||
|
*/
|
||||||
|
AiKnowledgeSegmentDO getKnowledgeSegment(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取知识库段落列表
|
||||||
|
*
|
||||||
|
* @param ids 段落编号列表
|
||||||
|
* @return 段落列表
|
||||||
|
*/
|
||||||
|
List<AiKnowledgeSegmentDO> getKnowledgeSegmentList(Collection<Long> ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取知识库段落 Map
|
||||||
|
*
|
||||||
|
* @param ids 段落编号列表
|
||||||
|
* @return 段落 Map
|
||||||
|
*/
|
||||||
|
default Map<Long, AiKnowledgeSegmentDO> getKnowledgeSegmentMap(Collection<Long> ids) {
|
||||||
|
return convertMap(getKnowledgeSegmentList(ids), AiKnowledgeSegmentDO::getId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取段落分页
|
* 获取段落分页
|
||||||
*
|
*
|
||||||
@ -24,12 +57,39 @@ public interface AiKnowledgeSegmentService {
|
|||||||
*/
|
*/
|
||||||
PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO);
|
PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于 content 内容,切片创建多个段落
|
||||||
|
*
|
||||||
|
* @param documentId 知识库文档编号
|
||||||
|
* @param content 文档内容
|
||||||
|
*/
|
||||||
|
void createKnowledgeSegmentBySplitContent(Long documentId, String content);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【异步】基于 content 内容,切片创建多个段落
|
||||||
|
*
|
||||||
|
* @param documentId 知识库文档编号
|
||||||
|
* @param content 文档内容
|
||||||
|
*/
|
||||||
|
@Async
|
||||||
|
default void createKnowledgeSegmentBySplitContentAsync(Long documentId, String content) {
|
||||||
|
createKnowledgeSegmentBySplitContent(documentId, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建知识库段落
|
||||||
|
*
|
||||||
|
* @param createReqVO 创建信息
|
||||||
|
* @return 段落编号
|
||||||
|
*/
|
||||||
|
Long createKnowledgeSegment(AiKnowledgeSegmentSaveReqVO createReqVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新段落的内容
|
* 更新段落的内容
|
||||||
*
|
*
|
||||||
* @param reqVO 更新内容
|
* @param reqVO 更新内容
|
||||||
*/
|
*/
|
||||||
void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO);
|
void updateKnowledgeSegment(AiKnowledgeSegmentSaveReqVO reqVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新段落的状态
|
* 更新段落的状态
|
||||||
@ -39,11 +99,52 @@ public interface AiKnowledgeSegmentService {
|
|||||||
void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);
|
void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 召回段落
|
* 重新索引知识库下的所有文档段落
|
||||||
*
|
*
|
||||||
* @param reqVO 召回请求信息
|
* @param knowledgeId 知识库编号
|
||||||
* @return 召回的段落
|
|
||||||
*/
|
*/
|
||||||
List<AiKnowledgeSegmentDO> similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO);
|
void reindexKnowledgeSegmentByKnowledgeId(Long knowledgeId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【异步】重新索引知识库下的所有文档段落
|
||||||
|
*
|
||||||
|
* @param knowledgeId 知识库编号
|
||||||
|
*/
|
||||||
|
@Async
|
||||||
|
default void reindexByKnowledgeIdAsync(Long knowledgeId) {
|
||||||
|
reindexKnowledgeSegmentByKnowledgeId(knowledgeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文档编号删除段落
|
||||||
|
*
|
||||||
|
* @param documentId 文档编号
|
||||||
|
*/
|
||||||
|
void deleteKnowledgeSegmentByDocumentId(Long documentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索知识库段落,并返回结果
|
||||||
|
*
|
||||||
|
* @param reqBO 搜索请求信息
|
||||||
|
* @return 搜索结果段落列表
|
||||||
|
*/
|
||||||
|
List<AiKnowledgeSegmentSearchRespBO> searchKnowledgeSegment(AiKnowledgeSegmentSearchReqBO reqBO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 URL 内容,切片创建多个段落
|
||||||
|
*
|
||||||
|
* @param url URL 地址
|
||||||
|
* @param segmentMaxTokens 段落最大 Token 数
|
||||||
|
* @return 切片后的段落列表
|
||||||
|
*/
|
||||||
|
List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文档处理进度(多个)
|
||||||
|
*
|
||||||
|
* @param documentIds 文档编号列表
|
||||||
|
* @return 文档处理列表
|
||||||
|
*/
|
||||||
|
List<AiKnowledgeSegmentProcessRespVO> getKnowledgeSegmentProcessList(List<Long> documentIds);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,31 +2,39 @@ package cn.iocoder.yudao.module.ai.service.knowledge;
|
|||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.collection.ListUtil;
|
import cn.hutool.core.collection.ListUtil;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
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.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSaveReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper;
|
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper;
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
|
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
|
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.model.AiModelService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.ai.document.Document;
|
import org.springframework.ai.document.Document;
|
||||||
|
import org.springframework.ai.tokenizer.TokenCountEstimator;
|
||||||
|
import org.springframework.ai.transformer.splitter.TextSplitter;
|
||||||
|
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
|
||||||
import org.springframework.ai.vectorstore.SearchRequest;
|
import org.springframework.ai.vectorstore.SearchRequest;
|
||||||
import org.springframework.ai.vectorstore.VectorStore;
|
import org.springframework.ai.vectorstore.VectorStore;
|
||||||
import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
|
import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG;
|
||||||
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS;
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,15 +46,28 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGM
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService {
|
public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService {
|
||||||
|
|
||||||
|
private static final String VECTOR_STORE_METADATA_KNOWLEDGE_ID = "knowledgeId";
|
||||||
|
private static final String VECTOR_STORE_METADATA_DOCUMENT_ID = "documentId";
|
||||||
|
private static final String VECTOR_STORE_METADATA_SEGMENT_ID = "segmentId";
|
||||||
|
|
||||||
|
private static final Map<String, Class<?>> VECTOR_STORE_METADATA_TYPES = Map.of(
|
||||||
|
VECTOR_STORE_METADATA_KNOWLEDGE_ID, String.class,
|
||||||
|
VECTOR_STORE_METADATA_DOCUMENT_ID, String.class,
|
||||||
|
VECTOR_STORE_METADATA_SEGMENT_ID, String.class);
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiKnowledgeSegmentMapper segmentMapper;
|
private AiKnowledgeSegmentMapper segmentMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiKnowledgeService knowledgeService;
|
private AiKnowledgeService knowledgeService;
|
||||||
@Resource
|
@Resource
|
||||||
private AiChatModelService chatModelService;
|
@Lazy // 延迟加载,避免循环依赖
|
||||||
|
private AiKnowledgeDocumentService knowledgeDocumentService;
|
||||||
@Resource
|
@Resource
|
||||||
private AiApiKeyService apiKeyService;
|
private AiModelService modelService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TokenCountEstimator tokenCountEstimator;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO) {
|
public PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO) {
|
||||||
@ -54,67 +75,198 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO) {
|
public void createKnowledgeSegmentBySplitContent(Long documentId, String content) {
|
||||||
// 1. 校验
|
// 1. 校验
|
||||||
AiKnowledgeSegmentDO oldKnowledgeSegment = validateKnowledgeSegmentExists(reqVO.getId());
|
AiKnowledgeDocumentDO documentDO = knowledgeDocumentService.validateKnowledgeDocumentExists(documentId);
|
||||||
|
AiKnowledgeDO knowledgeDO = knowledgeService.validateKnowledgeExists(documentDO.getKnowledgeId());
|
||||||
|
VectorStore vectorStore = getVectorStoreById(knowledgeDO);
|
||||||
|
|
||||||
// 2.1 获取知识库向量实例
|
// 2. 文档切片
|
||||||
VectorStore vectorStore = knowledgeService.getVectorStoreById(oldKnowledgeSegment.getKnowledgeId());
|
List<Document> documentSegments = splitContentByToken(content, documentDO.getSegmentMaxTokens());
|
||||||
// 2.2 删除原向量
|
|
||||||
vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId()));
|
|
||||||
// 2.3 重新向量化
|
|
||||||
Document document = new Document(reqVO.getContent());
|
|
||||||
document.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, oldKnowledgeSegment.getKnowledgeId());
|
|
||||||
vectorStore.add(List.of(document));
|
|
||||||
|
|
||||||
// 3. 更新段落内容
|
// 3.1 存储切片
|
||||||
AiKnowledgeSegmentDO knowledgeSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class);
|
List<AiKnowledgeSegmentDO> segmentDOs = convertList(documentSegments, segment -> {
|
||||||
knowledgeSegment.setVectorId(document.getId());
|
if (StrUtil.isEmpty(segment.getText())) {
|
||||||
segmentMapper.updateById(knowledgeSegment);
|
return null;
|
||||||
|
}
|
||||||
|
return new AiKnowledgeSegmentDO().setKnowledgeId(documentDO.getKnowledgeId()).setDocumentId(documentId)
|
||||||
|
.setContent(segment.getText()).setContentLength(segment.getText().length())
|
||||||
|
.setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY)
|
||||||
|
.setTokens(tokenCountEstimator.estimate(segment.getText()))
|
||||||
|
.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||||
|
});
|
||||||
|
segmentMapper.insertBatch(segmentDOs);
|
||||||
|
// 3.2 切片向量化
|
||||||
|
for (int i = 0; i < documentSegments.size(); i++) {
|
||||||
|
Document segment = documentSegments.get(i);
|
||||||
|
AiKnowledgeSegmentDO segmentDO = segmentDOs.get(i);
|
||||||
|
writeVectorStore(vectorStore, segmentDO, segment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateKnowledgeSegment(AiKnowledgeSegmentSaveReqVO reqVO) {
|
||||||
|
// 1. 校验
|
||||||
|
AiKnowledgeSegmentDO oldSegment = validateKnowledgeSegmentExists(reqVO.getId());
|
||||||
|
|
||||||
|
// 2. 删除向量
|
||||||
|
VectorStore vectorStore = getVectorStoreById(oldSegment.getKnowledgeId());
|
||||||
|
deleteVectorStore(vectorStore, oldSegment);
|
||||||
|
|
||||||
|
// 3.1 更新切片
|
||||||
|
AiKnowledgeSegmentDO newSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class);
|
||||||
|
segmentMapper.updateById(newSegment);
|
||||||
|
// 3.2 重新向量化,必须开启状态
|
||||||
|
if (CommonStatusEnum.isEnable(oldSegment.getStatus())) {
|
||||||
|
newSegment.setKnowledgeId(oldSegment.getKnowledgeId()).setDocumentId(oldSegment.getDocumentId());
|
||||||
|
writeVectorStore(vectorStore, newSegment, new Document(newSegment.getContent()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteKnowledgeSegmentByDocumentId(Long documentId) {
|
||||||
|
// 1. 查询需要删除的段落
|
||||||
|
List<AiKnowledgeSegmentDO> segments = segmentMapper.selectListByDocumentId(documentId);
|
||||||
|
if (CollUtil.isEmpty(segments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 批量删除段落记录
|
||||||
|
segmentMapper.deleteByIds(convertList(segments, AiKnowledgeSegmentDO::getId));
|
||||||
|
|
||||||
|
// 3. 删除向量存储中的段落
|
||||||
|
VectorStore vectorStore = getVectorStoreById(segments.get(0).getKnowledgeId());
|
||||||
|
vectorStore.delete(convertList(segments, AiKnowledgeSegmentDO::getVectorId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
|
public void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
|
||||||
// 0 校验
|
// 1. 校验
|
||||||
AiKnowledgeSegmentDO oldKnowledgeSegment = validateKnowledgeSegmentExists(reqVO.getId());
|
AiKnowledgeSegmentDO segment = validateKnowledgeSegmentExists(reqVO.getId());
|
||||||
// 1 获取知识库向量实例
|
|
||||||
VectorStore vectorStore = knowledgeService.getVectorStoreById(oldKnowledgeSegment.getKnowledgeId());
|
|
||||||
AiKnowledgeSegmentDO knowledgeSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class);
|
|
||||||
|
|
||||||
if (Objects.equals(reqVO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
|
// 2. 获取知识库向量实例
|
||||||
// 2.1 启用重新向量化
|
VectorStore vectorStore = getVectorStoreById(segment.getKnowledgeId());
|
||||||
Document document = new Document(oldKnowledgeSegment.getContent());
|
|
||||||
document.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, oldKnowledgeSegment.getKnowledgeId());
|
// 3. 更新状态
|
||||||
vectorStore.add(List.of(document));
|
segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(reqVO.getId()).setStatus(reqVO.getStatus()));
|
||||||
knowledgeSegment.setVectorId(document.getId());
|
|
||||||
|
// 4. 更新向量
|
||||||
|
if (CommonStatusEnum.isEnable(reqVO.getStatus())) {
|
||||||
|
writeVectorStore(vectorStore, segment, new Document(segment.getContent()));
|
||||||
} else {
|
} else {
|
||||||
// 2.2 禁用删除向量
|
deleteVectorStore(vectorStore, segment);
|
||||||
vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId()));
|
|
||||||
knowledgeSegment.setVectorId("");
|
|
||||||
}
|
}
|
||||||
// 3 更新段落状态
|
|
||||||
segmentMapper.updateById(knowledgeSegment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AiKnowledgeSegmentDO> similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO) {
|
public void reindexKnowledgeSegmentByKnowledgeId(Long knowledgeId) {
|
||||||
|
// 1.1 校验知识库存在
|
||||||
|
AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(knowledgeId);
|
||||||
|
// 1.2 获取知识库向量实例
|
||||||
|
VectorStore vectorStore = getVectorStoreById(knowledge);
|
||||||
|
|
||||||
|
// 2.1 查询知识库下的所有启用状态的段落
|
||||||
|
List<AiKnowledgeSegmentDO> segments = segmentMapper.selectListByKnowledgeIdAndStatus(
|
||||||
|
knowledgeId, CommonStatusEnum.ENABLE.getStatus());
|
||||||
|
if (CollUtil.isEmpty(segments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 2.2 遍历所有段落,重新索引
|
||||||
|
for (AiKnowledgeSegmentDO segment : segments) {
|
||||||
|
// 删除旧的向量
|
||||||
|
deleteVectorStore(vectorStore, segment);
|
||||||
|
// 重新创建向量
|
||||||
|
writeVectorStore(vectorStore, segment, new Document(segment.getContent()));
|
||||||
|
}
|
||||||
|
log.info("[reindexKnowledgeSegmentByKnowledgeId][知识库({}) 重新索引完成,共处理 {} 个段落]",
|
||||||
|
knowledgeId, segments.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO, Document segment) {
|
||||||
|
// 1. 向量存储
|
||||||
|
// 为什么要 toString 呢?因为部分 VectorStore 实现,不支持 Long 类型,例如说 QdrantVectorStore
|
||||||
|
segment.getMetadata().put(VECTOR_STORE_METADATA_KNOWLEDGE_ID, segmentDO.getKnowledgeId().toString());
|
||||||
|
segment.getMetadata().put(VECTOR_STORE_METADATA_DOCUMENT_ID, segmentDO.getDocumentId().toString());
|
||||||
|
segment.getMetadata().put(VECTOR_STORE_METADATA_SEGMENT_ID, segmentDO.getId().toString());
|
||||||
|
vectorStore.add(List.of(segment));
|
||||||
|
|
||||||
|
// 2. 更新向量 ID
|
||||||
|
segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(segmentDO.getId()).setVectorId(segment.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO) {
|
||||||
|
// 1. 更新向量 ID
|
||||||
|
if (StrUtil.isEmpty(segmentDO.getVectorId())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(segmentDO.getId())
|
||||||
|
.setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY));
|
||||||
|
|
||||||
|
// 2. 删除向量
|
||||||
|
vectorStore.delete(List.of(segmentDO.getVectorId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AiKnowledgeSegmentSearchRespBO> searchKnowledgeSegment(AiKnowledgeSegmentSearchReqBO reqBO) {
|
||||||
// 1. 校验
|
// 1. 校验
|
||||||
AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqVO.getKnowledgeId());
|
AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqBO.getKnowledgeId());
|
||||||
AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId());
|
|
||||||
|
|
||||||
// 2. 获取向量存储实例
|
// 2.1 向量检索
|
||||||
VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId());
|
VectorStore vectorStore = getVectorStoreById(knowledge);
|
||||||
|
List<Document> documents = vectorStore.similaritySearch(SearchRequest.builder()
|
||||||
// 3.1 向量检索
|
.query(reqBO.getContent())
|
||||||
List<Document> documentList = vectorStore.similaritySearch(SearchRequest.query(reqVO.getContent())
|
.topK(ObjUtil.defaultIfNull(reqBO.getTopK(), knowledge.getTopK()))
|
||||||
.withTopK(knowledge.getTopK())
|
.similarityThreshold(
|
||||||
.withSimilarityThreshold(knowledge.getSimilarityThreshold())
|
ObjUtil.defaultIfNull(reqBO.getSimilarityThreshold(), knowledge.getSimilarityThreshold()))
|
||||||
.withFilterExpression(new FilterExpressionBuilder().eq(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, reqVO.getKnowledgeId()).build()));
|
.filterExpression(new FilterExpressionBuilder()
|
||||||
if (CollUtil.isEmpty(documentList)) {
|
.eq(VECTOR_STORE_METADATA_KNOWLEDGE_ID, reqBO.getKnowledgeId().toString())
|
||||||
|
.build())
|
||||||
|
.build());
|
||||||
|
if (CollUtil.isEmpty(documents)) {
|
||||||
return ListUtil.empty();
|
return ListUtil.empty();
|
||||||
}
|
}
|
||||||
// 3.2 段落召回
|
// 2.2 段落召回
|
||||||
return segmentMapper.selectListByVectorIds(CollUtil.getFieldValues(documentList, "id", String.class));
|
List<AiKnowledgeSegmentDO> segments = segmentMapper
|
||||||
|
.selectListByVectorIds(convertList(documents, Document::getId));
|
||||||
|
if (CollUtil.isEmpty(segments)) {
|
||||||
|
return ListUtil.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 增加召回次数
|
||||||
|
segmentMapper.updateRetrievalCountIncrByIds(convertList(segments, AiKnowledgeSegmentDO::getId));
|
||||||
|
|
||||||
|
// 4. 构建结果
|
||||||
|
List<AiKnowledgeSegmentSearchRespBO> result = convertList(segments, segment -> {
|
||||||
|
Document document = CollUtil.findOne(documents, // 找到对应的文档
|
||||||
|
doc -> Objects.equals(doc.getId(), segment.getVectorId()));
|
||||||
|
if (document == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return BeanUtils.toBean(segment, AiKnowledgeSegmentSearchRespBO.class)
|
||||||
|
.setScore(document.getScore());
|
||||||
|
});
|
||||||
|
result.sort((o1, o2) -> Double.compare(o2.getScore(), o1.getScore())); // 按照分数降序排序
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens) {
|
||||||
|
// 1. 读取 URL 内容
|
||||||
|
String content = knowledgeDocumentService.readUrl(url);
|
||||||
|
|
||||||
|
// 2. 文档切片
|
||||||
|
List<Document> documentSegments = splitContentByToken(content, segmentMaxTokens);
|
||||||
|
|
||||||
|
// 3. 转换为段落对象
|
||||||
|
return convertList(documentSegments, segment -> {
|
||||||
|
if (StrUtil.isEmpty(segment.getText())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new AiKnowledgeSegmentDO()
|
||||||
|
.setContent(segment.getText())
|
||||||
|
.setContentLength(segment.getText().length())
|
||||||
|
.setTokens(tokenCountEstimator.estimate(segment.getText()));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,4 +283,75 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
|
|||||||
return knowledgeSegment;
|
return knowledgeSegment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private VectorStore getVectorStoreById(AiKnowledgeDO knowledge) {
|
||||||
|
return modelService.getOrCreateVectorStore(knowledge.getEmbeddingModelId(), VECTOR_STORE_METADATA_TYPES);
|
||||||
|
}
|
||||||
|
|
||||||
|
private VectorStore getVectorStoreById(Long knowledgeId) {
|
||||||
|
AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(knowledgeId);
|
||||||
|
return getVectorStoreById(knowledge);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Document> splitContentByToken(String content, Integer segmentMaxTokens) {
|
||||||
|
TextSplitter textSplitter = buildTokenTextSplitter(segmentMaxTokens);
|
||||||
|
return textSplitter.apply(Collections.singletonList(new Document(content)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextSplitter buildTokenTextSplitter(Integer segmentMaxTokens) {
|
||||||
|
return TokenTextSplitter.builder()
|
||||||
|
.withChunkSize(segmentMaxTokens)
|
||||||
|
.withMinChunkSizeChars(Integer.MAX_VALUE) // 忽略字符的截断
|
||||||
|
.withMinChunkLengthToEmbed(1) // 允许的最小有效分段长度
|
||||||
|
.withMaxNumChunks(Integer.MAX_VALUE)
|
||||||
|
.withKeepSeparator(true) // 保留分隔符
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AiKnowledgeSegmentProcessRespVO> getKnowledgeSegmentProcessList(List<Long> documentIds) {
|
||||||
|
if (CollUtil.isEmpty(documentIds)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return segmentMapper.selectProcessList(documentIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long createKnowledgeSegment(AiKnowledgeSegmentSaveReqVO createReqVO) {
|
||||||
|
// 1.1 校验文档是否存在
|
||||||
|
AiKnowledgeDocumentDO document = knowledgeDocumentService
|
||||||
|
.validateKnowledgeDocumentExists(createReqVO.getDocumentId());
|
||||||
|
// 1.2 获取知识库信息
|
||||||
|
AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(document.getKnowledgeId());
|
||||||
|
// 1.3 校验 token 熟练
|
||||||
|
Integer tokens = tokenCountEstimator.estimate(createReqVO.getContent());
|
||||||
|
if (tokens > document.getSegmentMaxTokens()) {
|
||||||
|
throw exception(KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG, tokens, document.getSegmentMaxTokens());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 保存段落
|
||||||
|
AiKnowledgeSegmentDO segment = BeanUtils.toBean(createReqVO, AiKnowledgeSegmentDO.class)
|
||||||
|
.setKnowledgeId(knowledge.getId()).setDocumentId(document.getId())
|
||||||
|
.setContentLength(createReqVO.getContent().length()).setTokens(tokens)
|
||||||
|
.setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY)
|
||||||
|
.setRetrievalCount(0).setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||||
|
segmentMapper.insert(segment);
|
||||||
|
|
||||||
|
// 3. 向量化
|
||||||
|
writeVectorStore(getVectorStoreById(knowledge), segment, new Document(segment.getContent()));
|
||||||
|
return segment.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AiKnowledgeSegmentDO getKnowledgeSegment(Long id) {
|
||||||
|
return segmentMapper.selectById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AiKnowledgeSegmentDO> getKnowledgeSegmentList(Collection<Long> ids) {
|
||||||
|
if (CollUtil.isEmpty(ids)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return segmentMapper.selectBatchIds(ids);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package cn.iocoder.yudao.module.ai.service.knowledge;
|
package cn.iocoder.yudao.module.ai.service.knowledge;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
|
||||||
import org.springframework.ai.vectorstore.VectorStore;
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI 知识库-基础信息 Service 接口
|
* AI 知识库-基础信息 Service 接口
|
||||||
@ -18,18 +18,24 @@ public interface AiKnowledgeService {
|
|||||||
* 创建知识库
|
* 创建知识库
|
||||||
*
|
*
|
||||||
* @param createReqVO 创建信息
|
* @param createReqVO 创建信息
|
||||||
* @param userId 用户编号
|
|
||||||
* @return 编号
|
* @return 编号
|
||||||
*/
|
*/
|
||||||
Long createKnowledge(AiKnowledgeCreateReqVO createReqVO, Long userId);
|
Long createKnowledge(AiKnowledgeSaveReqVO createReqVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新知识库
|
* 更新知识库
|
||||||
*
|
*
|
||||||
* @param updateReqVO 更新信息
|
* @param updateReqVO 更新信息
|
||||||
* @param userId 用户编号
|
|
||||||
*/
|
*/
|
||||||
void updateKnowledge(AiKnowledgeUpdateReqVO updateReqVO, Long userId);
|
void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得知识库
|
||||||
|
*
|
||||||
|
* @param id 编号
|
||||||
|
* @return 知识库
|
||||||
|
*/
|
||||||
|
AiKnowledgeDO getKnowledge(Long id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验知识库是否存在
|
* 校验知识库是否存在
|
||||||
@ -41,18 +47,17 @@ public interface AiKnowledgeService {
|
|||||||
/**
|
/**
|
||||||
* 获得知识库分页
|
* 获得知识库分页
|
||||||
*
|
*
|
||||||
* @param userId 用户编号
|
|
||||||
* @param pageReqVO 分页查询
|
* @param pageReqVO 分页查询
|
||||||
* @return 知识库分页
|
* @return 知识库分页
|
||||||
*/
|
*/
|
||||||
PageResult<AiKnowledgeDO> getKnowledgePage(Long userId, AiKnowledgePageReqVO pageReqVO);
|
PageResult<AiKnowledgeDO> getKnowledgePage(AiKnowledgePageReqVO pageReqVO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据知识库编号获取向量存储实例
|
* 获得指定状态的知识库列表
|
||||||
*
|
*
|
||||||
* @param id 知识库编号
|
* @param status 状态
|
||||||
* @return 向量存储实例
|
* @return 知识库列表
|
||||||
*/
|
*/
|
||||||
VectorStore getVectorStoreById(Long id);
|
List<AiKnowledgeDO> getKnowledgeSimpleListByStatus(Integer status);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,19 @@ import cn.hutool.core.util.ObjUtil;
|
|||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
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.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper;
|
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper;
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
|
import cn.iocoder.yudao.module.ai.service.model.AiModelService;
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.ai.vectorstore.VectorStore;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_EXISTS;
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_EXISTS;
|
||||||
|
|
||||||
@ -33,36 +33,43 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
|
|||||||
private AiKnowledgeMapper knowledgeMapper;
|
private AiKnowledgeMapper knowledgeMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiChatModelService chatModelService;
|
private AiModelService modelService;
|
||||||
@Resource
|
@Resource
|
||||||
private AiApiKeyService apiKeyService;
|
private AiKnowledgeSegmentService knowledgeSegmentService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long createKnowledge(AiKnowledgeCreateReqVO createReqVO, Long userId) {
|
public Long createKnowledge(AiKnowledgeSaveReqVO createReqVO) {
|
||||||
// 1. 校验模型配置
|
// 1. 校验模型配置
|
||||||
AiChatModelDO model = chatModelService.validateChatModel(createReqVO.getModelId());
|
AiModelDO model = modelService.validateModel(createReqVO.getEmbeddingModelId());
|
||||||
|
|
||||||
// 2. 插入知识库
|
// 2. 插入知识库
|
||||||
AiKnowledgeDO knowledgeBase = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class)
|
AiKnowledgeDO knowledge = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class)
|
||||||
.setModel(model.getModel()).setUserId(userId).setStatus(CommonStatusEnum.ENABLE.getStatus());
|
.setEmbeddingModel(model.getModel());
|
||||||
knowledgeMapper.insert(knowledgeBase);
|
knowledgeMapper.insert(knowledge);
|
||||||
return knowledgeBase.getId();
|
return knowledge.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateKnowledge(AiKnowledgeUpdateReqVO updateReqVO, Long userId) {
|
public void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO) {
|
||||||
// 1.1 校验知识库存在
|
// 1.1 校验知识库存在
|
||||||
AiKnowledgeDO knowledgeBaseDO = validateKnowledgeExists(updateReqVO.getId());
|
AiKnowledgeDO oldKnowledge = validateKnowledgeExists(updateReqVO.getId());
|
||||||
if (ObjUtil.notEqual(knowledgeBaseDO.getUserId(), userId)) {
|
|
||||||
throw exception(KNOWLEDGE_NOT_EXISTS);
|
|
||||||
}
|
|
||||||
// 1.2 校验模型配置
|
// 1.2 校验模型配置
|
||||||
AiChatModelDO model = chatModelService.validateChatModel(updateReqVO.getModelId());
|
AiModelDO model = modelService.validateModel(updateReqVO.getEmbeddingModelId());
|
||||||
|
|
||||||
// 2. 更新知识库
|
// 2. 更新知识库
|
||||||
AiKnowledgeDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class);
|
AiKnowledgeDO updateObj = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class)
|
||||||
updateDO.setModel(model.getModel());
|
.setEmbeddingModel(model.getModel());
|
||||||
knowledgeMapper.updateById(updateDO);
|
knowledgeMapper.updateById(updateObj);
|
||||||
|
|
||||||
|
// 3. 如果模型变化,需要 reindex 所有的文档
|
||||||
|
if (ObjUtil.notEqual(oldKnowledge.getEmbeddingModelId(), updateReqVO.getEmbeddingModelId())) {
|
||||||
|
knowledgeSegmentService.reindexByKnowledgeIdAsync(updateReqVO.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AiKnowledgeDO getKnowledge(Long id) {
|
||||||
|
return knowledgeMapper.selectById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -75,16 +82,13 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<AiKnowledgeDO> getKnowledgePage(Long userId, AiKnowledgePageReqVO pageReqVO) {
|
public PageResult<AiKnowledgeDO> getKnowledgePage(AiKnowledgePageReqVO pageReqVO) {
|
||||||
return knowledgeMapper.selectPage(userId, pageReqVO);
|
return knowledgeMapper.selectPage(pageReqVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VectorStore getVectorStoreById(Long id) {
|
public List<AiKnowledgeDO> getKnowledgeSimpleListByStatus(Integer status) {
|
||||||
AiKnowledgeDO knowledge = validateKnowledgeExists(id);
|
return knowledgeMapper.selectListByStatus(status);
|
||||||
AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId());
|
|
||||||
// 创建或获取 VectorStore 对象
|
|
||||||
return apiKeyService.getOrCreateVectorStore(model.getKeyId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.service.knowledge.bo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 知识库段落搜索 Request BO
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class AiKnowledgeSegmentSearchReqBO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 知识库编号
|
||||||
|
*/
|
||||||
|
@NotNull(message = "知识库编号不能为空")
|
||||||
|
private Long knowledgeId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "内容不能为空")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大返回数量
|
||||||
|
*/
|
||||||
|
private Integer topK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 相似度阈值
|
||||||
|
*/
|
||||||
|
private Double similarityThreshold;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.service.knowledge.bo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 知识库段落搜索 Response BO
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class AiKnowledgeSegmentSearchRespBO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 段落编号
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
/**
|
||||||
|
* 文档编号
|
||||||
|
*/
|
||||||
|
private Long documentId;
|
||||||
|
/**
|
||||||
|
* 知识库编号
|
||||||
|
*/
|
||||||
|
private Long knowledgeId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
/**
|
||||||
|
* 内容长度
|
||||||
|
*/
|
||||||
|
private Integer contentLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token 数量
|
||||||
|
*/
|
||||||
|
private Integer tokens;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 相似度分数
|
||||||
|
*/
|
||||||
|
private Double score;
|
||||||
|
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
package cn.iocoder.yudao.module.ai.service.mindmap;
|
package cn.iocoder.yudao.module.ai.service.mindmap;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.util.ObjUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum;
|
||||||
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
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;
|
||||||
@ -12,14 +13,13 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
|||||||
import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.mysql.mindmap.AiMindMapMapper;
|
import cn.iocoder.yudao.module.ai.dal.mysql.mindmap.AiMindMapMapper;
|
||||||
import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;
|
import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;
|
||||||
import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
|
import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
|
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
|
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
|
import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.model.AiModelService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.ai.chat.messages.Message;
|
import org.springframework.ai.chat.messages.Message;
|
||||||
@ -38,7 +38,7 @@ import java.util.List;
|
|||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MIND_MAP_NOT_EXISTS;
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI 思维导图 Service 实现类
|
* AI 思维导图 Service 实现类
|
||||||
@ -50,9 +50,7 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MIND_MAP_NOT_E
|
|||||||
public class AiMindMapServiceImpl implements AiMindMapService {
|
public class AiMindMapServiceImpl implements AiMindMapService {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiApiKeyService apiKeyService;
|
private AiModelService modalService;
|
||||||
@Resource
|
|
||||||
private AiChatModelService chatModalService;
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiChatRoleService chatRoleService;
|
private AiChatRoleService chatRoleService;
|
||||||
|
|
||||||
@ -65,17 +63,17 @@ public class AiMindMapServiceImpl implements AiMindMapService {
|
|||||||
AiChatRoleDO role = CollUtil.getFirst(
|
AiChatRoleDO role = CollUtil.getFirst(
|
||||||
chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_MIND_MAP_ROLE.getName()));
|
chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_MIND_MAP_ROLE.getName()));
|
||||||
// 1.1 获取导图执行模型
|
// 1.1 获取导图执行模型
|
||||||
AiChatModelDO model = getModel(role);
|
AiModelDO model = getModel(role);
|
||||||
// 1.2 获取角色设定消息
|
// 1.2 获取角色设定消息
|
||||||
String systemMessage = role != null && StrUtil.isNotBlank(role.getSystemMessage())
|
String systemMessage = role != null && StrUtil.isNotBlank(role.getSystemMessage())
|
||||||
? role.getSystemMessage() : AiChatRoleEnum.AI_MIND_MAP_ROLE.getSystemMessage();
|
? role.getSystemMessage() : AiChatRoleEnum.AI_MIND_MAP_ROLE.getSystemMessage();
|
||||||
// 1.3 校验平台
|
// 1.3 校验平台
|
||||||
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
|
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
|
||||||
ChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
|
ChatModel chatModel = modalService.getChatModel(model.getId());
|
||||||
|
|
||||||
// 2. 插入思维导图信息
|
// 2. 插入思维导图信息
|
||||||
AiMindMapDO mindMapDO = BeanUtils.toBean(generateReqVO, AiMindMapDO.class,
|
AiMindMapDO mindMapDO = BeanUtils.toBean(generateReqVO, AiMindMapDO.class, mindMap -> mindMap.setUserId(userId)
|
||||||
mindMap -> mindMap.setUserId(userId).setModel(model.getModel()).setPlatform(platform.getPlatform()));
|
.setPlatform(platform.getPlatform()).setModelId(model.getId()).setModel(model.getModel()));
|
||||||
mindMapMapper.insert(mindMapDO);
|
mindMapMapper.insert(mindMapDO);
|
||||||
|
|
||||||
// 3.1 构建 Prompt,并进行调用
|
// 3.1 构建 Prompt,并进行调用
|
||||||
@ -85,7 +83,7 @@ public class AiMindMapServiceImpl implements AiMindMapService {
|
|||||||
// 3.2 流式返回
|
// 3.2 流式返回
|
||||||
StringBuffer contentBuffer = new StringBuffer();
|
StringBuffer contentBuffer = new StringBuffer();
|
||||||
return streamResponse.map(chunk -> {
|
return streamResponse.map(chunk -> {
|
||||||
String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null;
|
String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;
|
||||||
newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
|
newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
|
||||||
contentBuffer.append(newContent);
|
contentBuffer.append(newContent);
|
||||||
// 响应结果
|
// 响应结果
|
||||||
@ -103,7 +101,7 @@ public class AiMindMapServiceImpl implements AiMindMapService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Prompt buildPrompt(AiMindMapGenerateReqVO generateReqVO, AiChatModelDO model, String systemMessage) {
|
private Prompt buildPrompt(AiMindMapGenerateReqVO generateReqVO, AiModelDO model, String systemMessage) {
|
||||||
// 1. 构建 message 列表
|
// 1. 构建 message 列表
|
||||||
List<Message> chatMessages = buildMessages(generateReqVO, systemMessage);
|
List<Message> chatMessages = buildMessages(generateReqVO, systemMessage);
|
||||||
// 2. 构建 options 对象
|
// 2. 构建 options 对象
|
||||||
@ -123,15 +121,21 @@ public class AiMindMapServiceImpl implements AiMindMapService {
|
|||||||
return chatMessages;
|
return chatMessages;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AiChatModelDO getModel(AiChatRoleDO role) {
|
private AiModelDO getModel(AiChatRoleDO role) {
|
||||||
AiChatModelDO model = null;
|
AiModelDO model = null;
|
||||||
if (role != null && role.getModelId() != null) {
|
if (role != null && role.getModelId() != null) {
|
||||||
model = chatModalService.getChatModel(role.getModelId());
|
model = modalService.getModel(role.getModelId());
|
||||||
}
|
}
|
||||||
if (model == null) {
|
if (model == null) {
|
||||||
model = chatModalService.getRequiredDefaultChatModel();
|
model = modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());
|
||||||
|
}
|
||||||
|
// 校验模型存在、且合法
|
||||||
|
if (model == null) {
|
||||||
|
throw exception(MODEL_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
if (ObjUtil.notEqual(model.getType(), AiModelTypeEnum.CHAT.getType())) {
|
||||||
|
throw exception(MODEL_USE_TYPE_ERROR);
|
||||||
}
|
}
|
||||||
Assert.notNull(model, "[AI] 获取不到模型");
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +1,10 @@
|
|||||||
package cn.iocoder.yudao.module.ai.service.model;
|
package cn.iocoder.yudao.module.ai.service.model;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
|
||||||
import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
|
|
||||||
import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import org.springframework.ai.chat.model.ChatModel;
|
|
||||||
import org.springframework.ai.embedding.EmbeddingModel;
|
|
||||||
import org.springframework.ai.image.ImageModel;
|
|
||||||
import org.springframework.ai.vectorstore.VectorStore;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -75,58 +68,13 @@ public interface AiApiKeyService {
|
|||||||
*/
|
*/
|
||||||
List<AiApiKeyDO> getApiKeyList();
|
List<AiApiKeyDO> getApiKeyList();
|
||||||
|
|
||||||
// ========== 与 spring-ai 集成 ==========
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得 ChatModel 对象
|
* 获得默认的 API 密钥
|
||||||
*
|
|
||||||
* @param id 编号
|
|
||||||
* @return ChatModel 对象
|
|
||||||
*/
|
|
||||||
ChatModel getChatModel(Long id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得 ImageModel 对象
|
|
||||||
*
|
|
||||||
* TODO 可优化点:目前默认获取 platform 对应的第一个开启的配置用于绘画;后续可以支持配置选择
|
|
||||||
*
|
*
|
||||||
* @param platform 平台
|
* @param platform 平台
|
||||||
* @return ImageModel 对象
|
* @param status 状态
|
||||||
|
* @return API 密钥
|
||||||
*/
|
*/
|
||||||
ImageModel getImageModel(AiPlatformEnum platform);
|
AiApiKeyDO getRequiredDefaultApiKey(String platform, Integer status);
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得 MidjourneyApi 对象
|
|
||||||
*
|
|
||||||
* TODO 可优化点:目前默认获取 Midjourney 对应的第一个开启的配置用于绘画;后续可以支持配置选择
|
|
||||||
*
|
|
||||||
* @return MidjourneyApi 对象
|
|
||||||
*/
|
|
||||||
MidjourneyApi getMidjourneyApi();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得 SunoApi 对象
|
|
||||||
*
|
|
||||||
* TODO 可优化点:目前默认获取 Suno 对应的第一个开启的配置用于音乐;后续可以支持配置选择
|
|
||||||
*
|
|
||||||
* @return SunoApi 对象
|
|
||||||
*/
|
|
||||||
SunoApi getSunoApi();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得 EmbeddingModel 对象
|
|
||||||
*
|
|
||||||
* @param id 编号
|
|
||||||
* @return EmbeddingModel 对象
|
|
||||||
*/
|
|
||||||
EmbeddingModel getEmbeddingModel(Long id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得 VectorStore 对象
|
|
||||||
*
|
|
||||||
* @param id 编号
|
|
||||||
* @return VectorStore 对象
|
|
||||||
*/
|
|
||||||
VectorStore getOrCreateVectorStore(Long id);
|
|
||||||
|
|
||||||
}
|
}
|
@ -1,9 +1,5 @@
|
|||||||
package cn.iocoder.yudao.module.ai.service.model;
|
package cn.iocoder.yudao.module.ai.service.model;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
|
||||||
import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
|
|
||||||
import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
|
|
||||||
import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
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;
|
||||||
@ -12,17 +8,14 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveR
|
|||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper;
|
import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.ai.chat.model.ChatModel;
|
|
||||||
import org.springframework.ai.embedding.EmbeddingModel;
|
|
||||||
import org.springframework.ai.image.ImageModel;
|
|
||||||
import org.springframework.ai.vectorstore.VectorStore;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_KEY_DISABLE;
|
||||||
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_KEY_NOT_EXISTS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI API 密钥 Service 实现类
|
* AI API 密钥 Service 实现类
|
||||||
@ -36,9 +29,6 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
|
|||||||
@Resource
|
@Resource
|
||||||
private AiApiKeyMapper apiKeyMapper;
|
private AiApiKeyMapper apiKeyMapper;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private AiModelFactory modelFactory;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long createApiKey(AiApiKeySaveReqVO createReqVO) {
|
public Long createApiKey(AiApiKeySaveReqVO createReqVO) {
|
||||||
// 插入
|
// 插入
|
||||||
@ -97,57 +87,13 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
|
|||||||
return apiKeyMapper.selectList();
|
return apiKeyMapper.selectList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 与 spring-ai 集成 ==========
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChatModel getChatModel(Long id) {
|
public AiApiKeyDO getRequiredDefaultApiKey(String platform, Integer status) {
|
||||||
AiApiKeyDO apiKey = validateApiKey(id);
|
AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform, status);
|
||||||
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
|
|
||||||
return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ImageModel getImageModel(AiPlatformEnum platform) {
|
|
||||||
AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
|
|
||||||
if (apiKey == null) {
|
if (apiKey == null) {
|
||||||
throw exception(API_KEY_IMAGE_NODE_FOUND, platform.getName());
|
throw exception(API_KEY_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
return modelFactory.getOrCreateImageModel(platform, apiKey.getApiKey(), apiKey.getUrl());
|
return apiKey;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MidjourneyApi getMidjourneyApi() {
|
|
||||||
AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(
|
|
||||||
AiPlatformEnum.MIDJOURNEY.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
|
|
||||||
if (apiKey == null) {
|
|
||||||
throw exception(API_KEY_MIDJOURNEY_NOT_FOUND);
|
|
||||||
}
|
|
||||||
return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SunoApi getSunoApi() {
|
|
||||||
AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(
|
|
||||||
AiPlatformEnum.SUNO.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
|
|
||||||
if (apiKey == null) {
|
|
||||||
throw exception(API_KEY_SUNO_NOT_FOUND);
|
|
||||||
}
|
|
||||||
return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public EmbeddingModel getEmbeddingModel(Long id) {
|
|
||||||
AiApiKeyDO apiKey = validateApiKey(id);
|
|
||||||
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
|
|
||||||
return modelFactory.getOrCreateEmbeddingModel(platform, apiKey.getApiKey(), apiKey.getUrl());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public VectorStore getOrCreateVectorStore(Long id) {
|
|
||||||
AiApiKeyDO apiKey = validateApiKey(id);
|
|
||||||
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
|
|
||||||
// 创建或获取 VectorStore 对象
|
|
||||||
return modelFactory.getOrCreateVectorStore(getEmbeddingModel(id), platform, apiKey.getApiKey(), apiKey.getUrl());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,92 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.ai.service.model;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
|
||||||
import jakarta.validation.Valid;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AI 聊天模型 Service 接口
|
|
||||||
*
|
|
||||||
* @author fansili
|
|
||||||
* @since 2024/4/24 19:42
|
|
||||||
*/
|
|
||||||
public interface AiChatModelService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建聊天模型
|
|
||||||
*
|
|
||||||
* @param createReqVO 创建信息
|
|
||||||
* @return 编号
|
|
||||||
*/
|
|
||||||
Long createChatModel(@Valid AiChatModelSaveReqVO createReqVO);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新聊天模型
|
|
||||||
*
|
|
||||||
* @param updateReqVO 更新信息
|
|
||||||
*/
|
|
||||||
void updateChatModel(@Valid AiChatModelSaveReqVO updateReqVO);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除聊天模型
|
|
||||||
*
|
|
||||||
* @param id 编号
|
|
||||||
*/
|
|
||||||
void deleteChatModel(Long id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得聊天模型
|
|
||||||
*
|
|
||||||
* @param id 编号
|
|
||||||
* @return 聊天模型
|
|
||||||
*/
|
|
||||||
AiChatModelDO getChatModel(Long id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得默认的聊天模型
|
|
||||||
*
|
|
||||||
* 如果获取不到,则抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 业务异常
|
|
||||||
*
|
|
||||||
* @return 聊天模型
|
|
||||||
*/
|
|
||||||
AiChatModelDO getRequiredDefaultChatModel();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得聊天模型分页
|
|
||||||
*
|
|
||||||
* @param pageReqVO 分页查询
|
|
||||||
* @return 聊天模型分页
|
|
||||||
*/
|
|
||||||
PageResult<AiChatModelDO> getChatModelPage(AiChatModelPageReqVO pageReqVO);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验聊天模型
|
|
||||||
*
|
|
||||||
* @param id 编号
|
|
||||||
* @return 聊天模型
|
|
||||||
*/
|
|
||||||
AiChatModelDO validateChatModel(Long id);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得聊天模型列表
|
|
||||||
*
|
|
||||||
* @param status 状态
|
|
||||||
* @return 聊天模型列表
|
|
||||||
*/
|
|
||||||
List<AiChatModelDO> getChatModelListByStatus(Integer status);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得聊天模型列表
|
|
||||||
*
|
|
||||||
* @param ids 编号数组
|
|
||||||
* @return 模型列表
|
|
||||||
*/
|
|
||||||
List<AiChatModelDO> getChatModelList(Collection<Long> ids);
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.ai.service.model;
|
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO;
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatModelMapper;
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
||||||
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AI 聊天模型 Service 实现类
|
|
||||||
*
|
|
||||||
* @author fansili
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
@Validated
|
|
||||||
public class AiChatModelServiceImpl implements AiChatModelService {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private AiApiKeyService apiKeyService;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private AiChatModelMapper chatModelMapper;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Long createChatModel(AiChatModelSaveReqVO createReqVO) {
|
|
||||||
// 1. 校验
|
|
||||||
AiPlatformEnum.validatePlatform(createReqVO.getPlatform());
|
|
||||||
apiKeyService.validateApiKey(createReqVO.getKeyId());
|
|
||||||
|
|
||||||
// 2. 插入
|
|
||||||
AiChatModelDO chatModel = BeanUtils.toBean(createReqVO, AiChatModelDO.class);
|
|
||||||
chatModelMapper.insert(chatModel);
|
|
||||||
return chatModel.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateChatModel(AiChatModelSaveReqVO updateReqVO) {
|
|
||||||
// 1. 校验
|
|
||||||
validateChatModelExists(updateReqVO.getId());
|
|
||||||
AiPlatformEnum.validatePlatform(updateReqVO.getPlatform());
|
|
||||||
apiKeyService.validateApiKey(updateReqVO.getKeyId());
|
|
||||||
|
|
||||||
// 2. 更新
|
|
||||||
AiChatModelDO updateObj = BeanUtils.toBean(updateReqVO, AiChatModelDO.class);
|
|
||||||
chatModelMapper.updateById(updateObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteChatModel(Long id) {
|
|
||||||
// 校验存在
|
|
||||||
validateChatModelExists(id);
|
|
||||||
// 删除
|
|
||||||
chatModelMapper.deleteById(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AiChatModelDO validateChatModelExists(Long id) {
|
|
||||||
AiChatModelDO model = chatModelMapper.selectById(id);
|
|
||||||
if (chatModelMapper.selectById(id) == null) {
|
|
||||||
throw exception(CHAT_MODEL_NOT_EXISTS);
|
|
||||||
}
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AiChatModelDO getChatModel(Long id) {
|
|
||||||
return chatModelMapper.selectById(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AiChatModelDO getRequiredDefaultChatModel() {
|
|
||||||
AiChatModelDO model = chatModelMapper.selectFirstByStatus(CommonStatusEnum.ENABLE.getStatus());
|
|
||||||
if (model == null) {
|
|
||||||
throw exception(CHAT_MODEL_DEFAULT_NOT_EXISTS);
|
|
||||||
}
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PageResult<AiChatModelDO> getChatModelPage(AiChatModelPageReqVO pageReqVO) {
|
|
||||||
return chatModelMapper.selectPage(pageReqVO);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AiChatModelDO validateChatModel(Long id) {
|
|
||||||
AiChatModelDO model = validateChatModelExists(id);
|
|
||||||
if (CommonStatusEnum.isDisable(model.getStatus())) {
|
|
||||||
throw exception(CHAT_MODEL_DISABLE);
|
|
||||||
}
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<AiChatModelDO> getChatModelListByStatus(Integer status) {
|
|
||||||
return chatModelMapper.selectList(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<AiChatModelDO> getChatModelList(Collection<Long> ids) {
|
|
||||||
return chatModelMapper.selectBatchIds(ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleS
|
|||||||
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatRoleMapper;
|
import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatRoleMapper;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -21,7 +22,8 @@ import java.util.List;
|
|||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_ROLE_DISABLE;
|
||||||
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_ROLE_NOT_EXISTS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI 聊天角色 Service 实现类
|
* AI 聊天角色 Service 实现类
|
||||||
@ -35,8 +37,19 @@ public class AiChatRoleServiceImpl implements AiChatRoleService {
|
|||||||
@Resource
|
@Resource
|
||||||
private AiChatRoleMapper chatRoleMapper;
|
private AiChatRoleMapper chatRoleMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AiKnowledgeService knowledgeService;
|
||||||
|
@Resource
|
||||||
|
private AiToolService toolService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long createChatRole(AiChatRoleSaveReqVO createReqVO) {
|
public Long createChatRole(AiChatRoleSaveReqVO createReqVO) {
|
||||||
|
// 校验文档
|
||||||
|
validateDocuments(createReqVO.getKnowledgeIds());
|
||||||
|
// 校验工具
|
||||||
|
validateTools(createReqVO.getToolIds());
|
||||||
|
|
||||||
|
// 保存角色
|
||||||
AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class);
|
AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class);
|
||||||
chatRoleMapper.insert(chatRole);
|
chatRoleMapper.insert(chatRole);
|
||||||
return chatRole.getId();
|
return chatRole.getId();
|
||||||
@ -44,6 +57,12 @@ public class AiChatRoleServiceImpl implements AiChatRoleService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId) {
|
public Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId) {
|
||||||
|
// 校验文档
|
||||||
|
validateDocuments(createReqVO.getKnowledgeIds());
|
||||||
|
// 校验工具
|
||||||
|
validateTools(createReqVO.getToolIds());
|
||||||
|
|
||||||
|
// 保存角色
|
||||||
AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class).setUserId(userId)
|
AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class).setUserId(userId)
|
||||||
.setStatus(CommonStatusEnum.ENABLE.getStatus()).setPublicStatus(false);
|
.setStatus(CommonStatusEnum.ENABLE.getStatus()).setPublicStatus(false);
|
||||||
chatRoleMapper.insert(chatRole);
|
chatRoleMapper.insert(chatRole);
|
||||||
@ -54,7 +73,12 @@ public class AiChatRoleServiceImpl implements AiChatRoleService {
|
|||||||
public void updateChatRole(AiChatRoleSaveReqVO updateReqVO) {
|
public void updateChatRole(AiChatRoleSaveReqVO updateReqVO) {
|
||||||
// 校验存在
|
// 校验存在
|
||||||
validateChatRoleExists(updateReqVO.getId());
|
validateChatRoleExists(updateReqVO.getId());
|
||||||
// 更新
|
// 校验文档
|
||||||
|
validateDocuments(updateReqVO.getKnowledgeIds());
|
||||||
|
// 校验工具
|
||||||
|
validateTools(updateReqVO.getToolIds());
|
||||||
|
|
||||||
|
// 更新角色
|
||||||
AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class);
|
AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class);
|
||||||
chatRoleMapper.updateById(updateObj);
|
chatRoleMapper.updateById(updateObj);
|
||||||
}
|
}
|
||||||
@ -66,12 +90,42 @@ public class AiChatRoleServiceImpl implements AiChatRoleService {
|
|||||||
if (ObjectUtil.notEqual(chatRole.getUserId(), userId)) {
|
if (ObjectUtil.notEqual(chatRole.getUserId(), userId)) {
|
||||||
throw exception(CHAT_ROLE_NOT_EXISTS);
|
throw exception(CHAT_ROLE_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
// 校验文档
|
||||||
|
validateDocuments(updateReqVO.getKnowledgeIds());
|
||||||
|
// 校验工具
|
||||||
|
validateTools(updateReqVO.getToolIds());
|
||||||
|
|
||||||
// 更新
|
// 更新
|
||||||
AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class);
|
AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class);
|
||||||
chatRoleMapper.updateById(updateObj);
|
chatRoleMapper.updateById(updateObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验知识库是否存在
|
||||||
|
*
|
||||||
|
* @param knowledgeIds 知识库编号列表
|
||||||
|
*/
|
||||||
|
private void validateDocuments(List<Long> knowledgeIds) {
|
||||||
|
if (CollUtil.isEmpty(knowledgeIds)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 校验文档是否存在
|
||||||
|
knowledgeIds.forEach(knowledgeService::validateKnowledgeExists);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验工具是否存在
|
||||||
|
*
|
||||||
|
* @param toolIds 工具编号列表
|
||||||
|
*/
|
||||||
|
private void validateTools(List<Long> toolIds) {
|
||||||
|
if (CollUtil.isEmpty(toolIds)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 遍历校验每个工具是否存在
|
||||||
|
toolIds.forEach(toolService::validateToolExists);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteChatRole(Long id) {
|
public void deleteChatRole(Long id) {
|
||||||
// 校验存在
|
// 校验存在
|
||||||
@ -134,7 +188,8 @@ public class AiChatRoleServiceImpl implements AiChatRoleService {
|
|||||||
@Override
|
@Override
|
||||||
public List<String> getChatRoleCategoryList() {
|
public List<String> getChatRoleCategoryList() {
|
||||||
List<AiChatRoleDO> list = chatRoleMapper.selectListGroupByCategory(CommonStatusEnum.ENABLE.getStatus());
|
List<AiChatRoleDO> list = chatRoleMapper.selectListGroupByCategory(CommonStatusEnum.ENABLE.getStatus());
|
||||||
return convertList(list, AiChatRoleDO::getCategory, role -> role != null && StrUtil.isNotBlank(role.getCategory()));
|
return convertList(list, AiChatRoleDO::getCategory,
|
||||||
|
role -> role != null && StrUtil.isNotBlank(role.getCategory()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -143,4 +198,3 @@ public class AiChatRoleServiceImpl implements AiChatRoleService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,134 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.service.model;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
|
||||||
|
import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.ai.chat.model.ChatModel;
|
||||||
|
import org.springframework.ai.image.ImageModel;
|
||||||
|
import org.springframework.ai.vectorstore.VectorStore;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 模型 Service 接口
|
||||||
|
*
|
||||||
|
* @author fansili
|
||||||
|
* @since 2024/4/24 19:42
|
||||||
|
*/
|
||||||
|
public interface AiModelService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建模型
|
||||||
|
*
|
||||||
|
* @param createReqVO 创建信息
|
||||||
|
* @return 编号
|
||||||
|
*/
|
||||||
|
Long createModel(@Valid AiModelSaveReqVO createReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新模型
|
||||||
|
*
|
||||||
|
* @param updateReqVO 更新信息
|
||||||
|
*/
|
||||||
|
void updateModel(@Valid AiModelSaveReqVO updateReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除模型
|
||||||
|
*
|
||||||
|
* @param id 编号
|
||||||
|
*/
|
||||||
|
void deleteModel(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得模型
|
||||||
|
*
|
||||||
|
* @param id 编号
|
||||||
|
* @return 模型
|
||||||
|
*/
|
||||||
|
AiModelDO getModel(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得默认的模型
|
||||||
|
*
|
||||||
|
* 如果获取不到,则抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 业务异常
|
||||||
|
*
|
||||||
|
* @return 模型
|
||||||
|
*/
|
||||||
|
AiModelDO getRequiredDefaultModel(Integer type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得模型分页
|
||||||
|
*
|
||||||
|
* @param pageReqVO 分页查询
|
||||||
|
* @return 模型分页
|
||||||
|
*/
|
||||||
|
PageResult<AiModelDO> getModelPage(AiModelPageReqVO pageReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验模型是否可使用
|
||||||
|
*
|
||||||
|
* @param id 编号
|
||||||
|
* @return 模型
|
||||||
|
*/
|
||||||
|
AiModelDO validateModel(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得模型列表
|
||||||
|
*
|
||||||
|
* @param status 状态
|
||||||
|
* @param type 类型
|
||||||
|
* @param platform 平台,允许空
|
||||||
|
* @return 模型列表
|
||||||
|
*/
|
||||||
|
List<AiModelDO> getModelListByStatusAndType(Integer status, Integer type,
|
||||||
|
@Nullable String platform);
|
||||||
|
|
||||||
|
// ========== 与 Spring AI 集成 ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得 ChatModel 对象
|
||||||
|
*
|
||||||
|
* @param id 编号
|
||||||
|
* @return ChatModel 对象
|
||||||
|
*/
|
||||||
|
ChatModel getChatModel(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得 ImageModel 对象
|
||||||
|
*
|
||||||
|
* @param id 编号
|
||||||
|
* @return ImageModel 对象
|
||||||
|
*/
|
||||||
|
ImageModel getImageModel(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得 MidjourneyApi 对象
|
||||||
|
*
|
||||||
|
* @param id 编号
|
||||||
|
* @return MidjourneyApi 对象
|
||||||
|
*/
|
||||||
|
MidjourneyApi getMidjourneyApi(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得 SunoApi 对象
|
||||||
|
*
|
||||||
|
* @return SunoApi 对象
|
||||||
|
*/
|
||||||
|
SunoApi getSunoApi();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得 VectorStore 对象
|
||||||
|
*
|
||||||
|
* @param id 编号
|
||||||
|
* @param metadataFields 元数据的定义
|
||||||
|
* @return VectorStore 对象
|
||||||
|
*/
|
||||||
|
VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,171 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.service.model;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
||||||
|
import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
|
||||||
|
import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
|
||||||
|
import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatMapper;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.ai.chat.model.ChatModel;
|
||||||
|
import org.springframework.ai.embedding.EmbeddingModel;
|
||||||
|
import org.springframework.ai.image.ImageModel;
|
||||||
|
import org.springframework.ai.vectorstore.SimpleVectorStore;
|
||||||
|
import org.springframework.ai.vectorstore.VectorStore;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 模型 Service 实现类
|
||||||
|
*
|
||||||
|
* @author fansili
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Validated
|
||||||
|
public class AiModelServiceImpl implements AiModelService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AiApiKeyService apiKeyService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AiChatMapper modelMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AiModelFactory modelFactory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long createModel(AiModelSaveReqVO createReqVO) {
|
||||||
|
// 1. 校验
|
||||||
|
AiPlatformEnum.validatePlatform(createReqVO.getPlatform());
|
||||||
|
apiKeyService.validateApiKey(createReqVO.getKeyId());
|
||||||
|
|
||||||
|
// 2. 插入
|
||||||
|
AiModelDO model = BeanUtils.toBean(createReqVO, AiModelDO.class);
|
||||||
|
modelMapper.insert(model);
|
||||||
|
return model.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateModel(AiModelSaveReqVO updateReqVO) {
|
||||||
|
// 1. 校验
|
||||||
|
validateModelExists(updateReqVO.getId());
|
||||||
|
AiPlatformEnum.validatePlatform(updateReqVO.getPlatform());
|
||||||
|
apiKeyService.validateApiKey(updateReqVO.getKeyId());
|
||||||
|
|
||||||
|
// 2. 更新
|
||||||
|
AiModelDO updateObj = BeanUtils.toBean(updateReqVO, AiModelDO.class);
|
||||||
|
modelMapper.updateById(updateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteModel(Long id) {
|
||||||
|
// 校验存在
|
||||||
|
validateModelExists(id);
|
||||||
|
// 删除
|
||||||
|
modelMapper.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AiModelDO validateModelExists(Long id) {
|
||||||
|
AiModelDO model = modelMapper.selectById(id);
|
||||||
|
if (modelMapper.selectById(id) == null) {
|
||||||
|
throw exception(MODEL_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AiModelDO getModel(Long id) {
|
||||||
|
return modelMapper.selectById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AiModelDO getRequiredDefaultModel(Integer type) {
|
||||||
|
AiModelDO model = modelMapper.selectFirstByStatus(type, CommonStatusEnum.ENABLE.getStatus());
|
||||||
|
if (model == null) {
|
||||||
|
throw exception(MODEL_DEFAULT_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<AiModelDO> getModelPage(AiModelPageReqVO pageReqVO) {
|
||||||
|
return modelMapper.selectPage(pageReqVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AiModelDO validateModel(Long id) {
|
||||||
|
AiModelDO model = validateModelExists(id);
|
||||||
|
if (CommonStatusEnum.isDisable(model.getStatus())) {
|
||||||
|
throw exception(MODEL_DISABLE);
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AiModelDO> getModelListByStatusAndType(Integer status, Integer type, String platform) {
|
||||||
|
return modelMapper.selectListByStatusAndType(status, type, platform);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 与 Spring AI 集成 ==========
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChatModel getChatModel(Long id) {
|
||||||
|
AiModelDO model = validateModel(id);
|
||||||
|
AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
|
||||||
|
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
|
||||||
|
return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageModel getImageModel(Long id) {
|
||||||
|
AiModelDO model = validateModel(id);
|
||||||
|
AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
|
||||||
|
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
|
||||||
|
return modelFactory.getOrCreateImageModel(platform, apiKey.getApiKey(), apiKey.getUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MidjourneyApi getMidjourneyApi(Long id) {
|
||||||
|
AiModelDO model = validateModel(id);
|
||||||
|
AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
|
||||||
|
return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SunoApi getSunoApi() {
|
||||||
|
AiApiKeyDO apiKey = apiKeyService.getRequiredDefaultApiKey(
|
||||||
|
AiPlatformEnum.SUNO.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
|
||||||
|
return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields) {
|
||||||
|
// 获取模型 + 密钥
|
||||||
|
AiModelDO model = validateModel(id);
|
||||||
|
AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
|
||||||
|
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
|
||||||
|
|
||||||
|
// 创建或获取 EmbeddingModel 对象
|
||||||
|
EmbeddingModel embeddingModel = modelFactory.getOrCreateEmbeddingModel(
|
||||||
|
platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel());
|
||||||
|
|
||||||
|
// 创建或获取 VectorStore 对象
|
||||||
|
return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel, metadataFields);
|
||||||
|
// return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel, metadataFields);
|
||||||
|
// return modelFactory.getOrCreateVectorStore(RedisVectorStore.class, embeddingModel, metadataFields);
|
||||||
|
// return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.service.model;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 工具 Service 接口
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
public interface AiToolService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建工具
|
||||||
|
*
|
||||||
|
* @param createReqVO 创建信息
|
||||||
|
* @return 编号
|
||||||
|
*/
|
||||||
|
Long createTool(@Valid AiToolSaveReqVO createReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新工具
|
||||||
|
*
|
||||||
|
* @param updateReqVO 更新信息
|
||||||
|
*/
|
||||||
|
void updateTool(@Valid AiToolSaveReqVO updateReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除工具
|
||||||
|
*
|
||||||
|
* @param id 编号
|
||||||
|
*/
|
||||||
|
void deleteTool(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验工具是否存在
|
||||||
|
*
|
||||||
|
* @param id 编号
|
||||||
|
*/
|
||||||
|
void validateToolExists(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得工具
|
||||||
|
*
|
||||||
|
* @param id 编号
|
||||||
|
* @return 工具
|
||||||
|
*/
|
||||||
|
AiToolDO getTool(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得工具列表
|
||||||
|
*
|
||||||
|
* @param ids 编号列表
|
||||||
|
* @return 工具列表
|
||||||
|
*/
|
||||||
|
List<AiToolDO> getToolList(Collection<Long> ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得工具分页
|
||||||
|
*
|
||||||
|
* @param pageReqVO 分页查询
|
||||||
|
* @return 工具分页
|
||||||
|
*/
|
||||||
|
PageResult<AiToolDO> getToolPage(AiToolPageReqVO pageReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得工具列表
|
||||||
|
*
|
||||||
|
* @param status 状态
|
||||||
|
* @return 工具列表
|
||||||
|
*/
|
||||||
|
List<AiToolDO> getToolListByStatus(Integer status);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.service.model;
|
||||||
|
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.mysql.model.AiToolMapper;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.TOOL_NAME_NOT_EXISTS;
|
||||||
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.TOOL_NOT_EXISTS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 工具 Service 实现类
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Validated
|
||||||
|
public class AiToolServiceImpl implements AiToolService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AiToolMapper toolMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long createTool(AiToolSaveReqVO createReqVO) {
|
||||||
|
// 校验名称是否存在
|
||||||
|
validateToolNameExists(createReqVO.getName());
|
||||||
|
|
||||||
|
// 插入
|
||||||
|
AiToolDO tool = BeanUtils.toBean(createReqVO, AiToolDO.class);
|
||||||
|
toolMapper.insert(tool);
|
||||||
|
return tool.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateTool(AiToolSaveReqVO updateReqVO) {
|
||||||
|
// 1.1 校验存在
|
||||||
|
validateToolExists(updateReqVO.getId());
|
||||||
|
// 1.2 校验名称是否存在
|
||||||
|
validateToolNameExists(updateReqVO.getName());
|
||||||
|
|
||||||
|
// 2. 更新
|
||||||
|
AiToolDO updateObj = BeanUtils.toBean(updateReqVO, AiToolDO.class);
|
||||||
|
toolMapper.updateById(updateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteTool(Long id) {
|
||||||
|
// 校验存在
|
||||||
|
validateToolExists(id);
|
||||||
|
// 删除
|
||||||
|
toolMapper.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validateToolExists(Long id) {
|
||||||
|
if (toolMapper.selectById(id) == null) {
|
||||||
|
throw exception(TOOL_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateToolNameExists(String name) {
|
||||||
|
try {
|
||||||
|
SpringUtil.getBean(name);
|
||||||
|
} catch (NoSuchBeanDefinitionException e) {
|
||||||
|
throw exception(TOOL_NAME_NOT_EXISTS, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AiToolDO getTool(Long id) {
|
||||||
|
return toolMapper.selectById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AiToolDO> getToolList(Collection<Long> ids) {
|
||||||
|
return toolMapper.selectBatchIds(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PageResult<AiToolDO> getToolPage(AiToolPageReqVO pageReqVO) {
|
||||||
|
return toolMapper.selectPage(pageReqVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AiToolDO> getToolListByStatus(Integer status) {
|
||||||
|
return toolMapper.selectListByStatus(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.service.model.tool;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonClassDescription;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具:列出指定目录的文件列表
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Component("directory_list")
|
||||||
|
public class DirectoryListToolFunction implements Function<DirectoryListToolFunction.Request, DirectoryListToolFunction.Response> {
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@JsonClassDescription("列出指定目录的文件列表")
|
||||||
|
public static class Request {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目录路径
|
||||||
|
*/
|
||||||
|
@JsonProperty(required = true, value = "path")
|
||||||
|
@JsonPropertyDescription("目录路径,例如说:/Users/yunai")
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public static class Response {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件列表
|
||||||
|
*/
|
||||||
|
private List<File> files;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class File {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为目录
|
||||||
|
*/
|
||||||
|
private Boolean directory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 大小,仅对文件有效
|
||||||
|
*/
|
||||||
|
private String size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最后修改时间
|
||||||
|
*/
|
||||||
|
private String lastModified;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response apply(Request request) {
|
||||||
|
// 校验目录存在
|
||||||
|
String path = StrUtil.blankToDefault(request.getPath(), "/");
|
||||||
|
if (!FileUtil.exist(path) || !FileUtil.isDirectory(path)) {
|
||||||
|
return new Response(Collections.emptyList());
|
||||||
|
}
|
||||||
|
// 列出目录
|
||||||
|
File[] files = FileUtil.ls(path);
|
||||||
|
if (ArrayUtil.isEmpty(files)) {
|
||||||
|
return new Response(Collections.emptyList());
|
||||||
|
}
|
||||||
|
return new Response(convertList(Arrays.asList(files), file ->
|
||||||
|
new Response.File().setDirectory(file.isDirectory()).setName(file.getName())
|
||||||
|
.setLastModified(LocalDateTimeUtil.format(LocalDateTimeUtil.of(file.lastModified()), NORM_DATETIME_PATTERN))));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,118 @@
|
|||||||
|
package cn.iocoder.yudao.module.ai.service.model.tool;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||||
|
import cn.hutool.core.util.RandomUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonClassDescription;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工具:查询指定城市的天气信息
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@Component("weather_query")
|
||||||
|
public class WeatherQueryToolFunction
|
||||||
|
implements Function<WeatherQueryToolFunction.Request, WeatherQueryToolFunction.Response> {
|
||||||
|
|
||||||
|
private static final String[] WEATHER_CONDITIONS = { "晴朗", "多云", "阴天", "小雨", "大雨", "雷雨", "小雪", "大雪" };
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@JsonClassDescription("查询指定城市的天气信息")
|
||||||
|
public static class Request {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 城市名称
|
||||||
|
*/
|
||||||
|
@JsonProperty(required = true, value = "city")
|
||||||
|
@JsonPropertyDescription("城市名称,例如:北京、上海、广州")
|
||||||
|
private String city;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public static class Response {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 城市名称
|
||||||
|
*/
|
||||||
|
private String city;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 天气信息
|
||||||
|
*/
|
||||||
|
private WeatherInfo weatherInfo;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public static class WeatherInfo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 温度(摄氏度)
|
||||||
|
*/
|
||||||
|
private Integer temperature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 天气状况
|
||||||
|
*/
|
||||||
|
private String condition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 湿度百分比
|
||||||
|
*/
|
||||||
|
private Integer humidity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 风速(km/h)
|
||||||
|
*/
|
||||||
|
private Integer windSpeed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询时间
|
||||||
|
*/
|
||||||
|
private String queryTime;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response apply(Request request) {
|
||||||
|
// 检查城市名称是否为空
|
||||||
|
if (StrUtil.isBlank(request.getCity())) {
|
||||||
|
return new Response("未知城市", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取天气数据
|
||||||
|
String city = request.getCity();
|
||||||
|
Response.WeatherInfo weatherInfo = generateMockWeatherInfo();
|
||||||
|
return new Response(city, weatherInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成模拟的天气数据
|
||||||
|
* 在实际应用中,应替换为真实 API 调用
|
||||||
|
*/
|
||||||
|
private Response.WeatherInfo generateMockWeatherInfo() {
|
||||||
|
int temperature = RandomUtil.randomInt(-5, 30);
|
||||||
|
int humidity = RandomUtil.randomInt(1, 100);
|
||||||
|
int windSpeed = RandomUtil.randomInt(1, 30);
|
||||||
|
String condition = RandomUtil.randomEle(WEATHER_CONDITIONS);
|
||||||
|
return new Response.WeatherInfo(temperature, condition, humidity, windSpeed,
|
||||||
|
LocalDateTimeUtil.format(LocalDateTime.now(), NORM_DATETIME_PATTERN));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,7 +16,7 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
|
|||||||
import cn.iocoder.yudao.module.ai.dal.mysql.music.AiMusicMapper;
|
import cn.iocoder.yudao.module.ai.dal.mysql.music.AiMusicMapper;
|
||||||
import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum;
|
import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum;
|
||||||
import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;
|
import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
|
import cn.iocoder.yudao.module.ai.service.model.AiModelService;
|
||||||
import cn.iocoder.yudao.module.infra.api.file.FileApi;
|
import cn.iocoder.yudao.module.infra.api.file.FileApi;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -41,7 +41,7 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MUSIC_NOT_EXIS
|
|||||||
public class AiMusicServiceImpl implements AiMusicService {
|
public class AiMusicServiceImpl implements AiMusicService {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiApiKeyService apiKeyService;
|
private AiModelService modelService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiMusicMapper musicMapper;
|
private AiMusicMapper musicMapper;
|
||||||
@ -53,7 +53,7 @@ public class AiMusicServiceImpl implements AiMusicService {
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO) {
|
public List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO) {
|
||||||
// 1. 调用 Suno 生成音乐
|
// 1. 调用 Suno 生成音乐
|
||||||
SunoApi sunoApi = apiKeyService.getSunoApi();
|
SunoApi sunoApi = modelService.getSunoApi();
|
||||||
List<SunoApi.MusicData> musicDataList;
|
List<SunoApi.MusicData> musicDataList;
|
||||||
if (Objects.equals(AiMusicGenerateModeEnum.DESCRIPTION.getMode(), reqVO.getGenerateMode())) {
|
if (Objects.equals(AiMusicGenerateModeEnum.DESCRIPTION.getMode(), reqVO.getGenerateMode())) {
|
||||||
// 1.1 描述模式
|
// 1.1 描述模式
|
||||||
@ -88,7 +88,7 @@ public class AiMusicServiceImpl implements AiMusicService {
|
|||||||
log.info("[syncMusic][Suno 开始同步, 共 ({}) 个任务]", streamingTask.size());
|
log.info("[syncMusic][Suno 开始同步, 共 ({}) 个任务]", streamingTask.size());
|
||||||
|
|
||||||
// GET 请求,为避免参数过长,分批次处理
|
// GET 请求,为避免参数过长,分批次处理
|
||||||
SunoApi sunoApi = apiKeyService.getSunoApi();
|
SunoApi sunoApi = modelService.getSunoApi();
|
||||||
CollUtil.split(streamingTask, 36).forEach(chunkList -> {
|
CollUtil.split(streamingTask, 36).forEach(chunkList -> {
|
||||||
Map<String, Long> taskIdMap = convertMap(chunkList, AiMusicDO::getTaskId, AiMusicDO::getId);
|
Map<String, Long> taskIdMap = convertMap(chunkList, AiMusicDO::getTaskId, AiMusicDO::getId);
|
||||||
List<SunoApi.MusicData> musicTaskList = sunoApi.getMusicList(new ArrayList<>(taskIdMap.keySet()));
|
List<SunoApi.MusicData> musicTaskList = sunoApi.getMusicList(new ArrayList<>(taskIdMap.keySet()));
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package cn.iocoder.yudao.module.ai.service.write;
|
package cn.iocoder.yudao.module.ai.service.write;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.util.ObjUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum;
|
||||||
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
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;
|
||||||
@ -11,17 +12,16 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|||||||
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
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.controller.admin.write.vo.AiWritePageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWritePageReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
|
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.mysql.write.AiWriteMapper;
|
import cn.iocoder.yudao.module.ai.dal.mysql.write.AiWriteMapper;
|
||||||
import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;
|
import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;
|
||||||
import cn.iocoder.yudao.module.ai.enums.DictTypeConstants;
|
import cn.iocoder.yudao.module.ai.enums.DictTypeConstants;
|
||||||
import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
|
import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
|
||||||
import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;
|
import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
|
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
|
|
||||||
import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
|
import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.model.AiModelService;
|
||||||
import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
|
import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -42,7 +42,7 @@ import java.util.Objects;
|
|||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WRITE_NOT_EXISTS;
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AI 写作 Service 实现类
|
* AI 写作 Service 实现类
|
||||||
@ -54,17 +54,15 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WRITE_NOT_EXIS
|
|||||||
public class AiWriteServiceImpl implements AiWriteService {
|
public class AiWriteServiceImpl implements AiWriteService {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiApiKeyService apiKeyService;
|
private AiModelService modalService;
|
||||||
@Resource
|
|
||||||
private AiChatModelService chatModalService;
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiChatRoleService chatRoleService;
|
private AiChatRoleService chatRoleService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private DictDataApi dictDataApi;
|
private AiWriteMapper writeMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private AiWriteMapper writeMapper;
|
private DictDataApi dictDataApi;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId) {
|
public Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId) {
|
||||||
@ -72,17 +70,17 @@ public class AiWriteServiceImpl implements AiWriteService {
|
|||||||
AiChatRoleDO writeRole = CollUtil.getFirst(
|
AiChatRoleDO writeRole = CollUtil.getFirst(
|
||||||
chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_WRITE_ROLE.getName()));
|
chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_WRITE_ROLE.getName()));
|
||||||
// 1.1 获取写作执行模型
|
// 1.1 获取写作执行模型
|
||||||
AiChatModelDO model = getModel(writeRole);
|
AiModelDO model = getModel(writeRole);
|
||||||
// 1.2 获取角色设定消息
|
// 1.2 获取角色设定消息
|
||||||
String systemMessage = Objects.nonNull(writeRole) && StrUtil.isNotBlank(writeRole.getSystemMessage())
|
String systemMessage = Objects.nonNull(writeRole) && StrUtil.isNotBlank(writeRole.getSystemMessage())
|
||||||
? writeRole.getSystemMessage() : AiChatRoleEnum.AI_WRITE_ROLE.getSystemMessage();
|
? writeRole.getSystemMessage() : AiChatRoleEnum.AI_WRITE_ROLE.getSystemMessage();
|
||||||
// 1.3 校验平台
|
// 1.3 校验平台
|
||||||
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
|
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
|
||||||
StreamingChatModel chatModel = apiKeyService.getChatModel(model.getKeyId());
|
StreamingChatModel chatModel = modalService.getChatModel(model.getKeyId());
|
||||||
|
|
||||||
// 2. 插入写作信息
|
// 2. 插入写作信息
|
||||||
AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class,
|
AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class, write -> write.setUserId(userId)
|
||||||
write -> write.setUserId(userId).setPlatform(platform.getPlatform()).setModel(model.getModel()));
|
.setPlatform(platform.getPlatform()).setModelId(model.getId()).setModel(model.getModel()));
|
||||||
writeMapper.insert(writeDO);
|
writeMapper.insert(writeDO);
|
||||||
|
|
||||||
// 3.1 构建 Prompt,并进行调用
|
// 3.1 构建 Prompt,并进行调用
|
||||||
@ -92,7 +90,7 @@ public class AiWriteServiceImpl implements AiWriteService {
|
|||||||
// 3.2 流式返回
|
// 3.2 流式返回
|
||||||
StringBuffer contentBuffer = new StringBuffer();
|
StringBuffer contentBuffer = new StringBuffer();
|
||||||
return streamResponse.map(chunk -> {
|
return streamResponse.map(chunk -> {
|
||||||
String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null;
|
String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;
|
||||||
newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
|
newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
|
||||||
contentBuffer.append(newContent);
|
contentBuffer.append(newContent);
|
||||||
// 响应结果
|
// 响应结果
|
||||||
@ -109,19 +107,25 @@ public class AiWriteServiceImpl implements AiWriteService {
|
|||||||
}).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));
|
}).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private AiChatModelDO getModel(AiChatRoleDO writeRole) {
|
private AiModelDO getModel(AiChatRoleDO writeRole) {
|
||||||
AiChatModelDO model = null;
|
AiModelDO model = null;
|
||||||
if (Objects.nonNull(writeRole) && Objects.nonNull(writeRole.getModelId())) {
|
if (Objects.nonNull(writeRole) && Objects.nonNull(writeRole.getModelId())) {
|
||||||
model = chatModalService.getChatModel(writeRole.getModelId());
|
model = modalService.getModel(writeRole.getModelId());
|
||||||
}
|
}
|
||||||
if (model == null) {
|
if (model == null) {
|
||||||
model = chatModalService.getRequiredDefaultChatModel();
|
model = modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());
|
||||||
|
}
|
||||||
|
// 校验模型存在、且合法
|
||||||
|
if (model == null) {
|
||||||
|
throw exception(MODEL_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
if (ObjUtil.notEqual(model.getType(), AiModelTypeEnum.CHAT.getType())) {
|
||||||
|
throw exception(MODEL_USE_TYPE_ERROR);
|
||||||
}
|
}
|
||||||
Assert.notNull(model, "[AI] 获取不到模型");
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Prompt buildPrompt(AiWriteGenerateReqVO generateReqVO, AiChatModelDO model, String systemMessage) {
|
private Prompt buildPrompt(AiWriteGenerateReqVO generateReqVO, AiModelDO model, String systemMessage) {
|
||||||
// 1. 构建 message 列表
|
// 1. 构建 message 列表
|
||||||
List<Message> chatMessages = buildMessages(generateReqVO, systemMessage);
|
List<Message> chatMessages = buildMessages(generateReqVO, systemMessage);
|
||||||
// 2. 构建 options 对象
|
// 2. 构建 options 对象
|
||||||
|
@ -14,47 +14,94 @@
|
|||||||
<name>${project.artifactId}</name>
|
<name>${project.artifactId}</name>
|
||||||
<description>AI 大模型拓展,接入国内外大模型</description>
|
<description>AI 大模型拓展,接入国内外大模型</description>
|
||||||
<properties>
|
<properties>
|
||||||
<spring-ai.groupId>group.springframework.ai</spring-ai.groupId>
|
<spring-ai.version>1.0.0-M6</spring-ai.version>
|
||||||
<spring-ai.version>1.1.0</spring-ai.version>
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${spring-ai.groupId}</groupId>
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
<artifactId>spring-ai-zhipuai-spring-boot-starter</artifactId>
|
<artifactId>yudao-common</artifactId>
|
||||||
<version>${spring-ai.version}</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring AI Model 模型接入 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${spring-ai.groupId}</groupId>
|
<groupId>org.springframework.ai</groupId>
|
||||||
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
|
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
|
||||||
<version>${spring-ai.version}</version>
|
<version>${spring-ai.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${spring-ai.groupId}</groupId>
|
<groupId>org.springframework.ai</groupId>
|
||||||
<artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId>
|
<artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId>
|
||||||
<version>${spring-ai.version}</version>
|
<version>${spring-ai.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${spring-ai.groupId}</groupId>
|
<groupId>org.springframework.ai</groupId>
|
||||||
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
|
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
|
||||||
<version>${spring-ai.version}</version>
|
<version>${spring-ai.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${spring-ai.groupId}</groupId>
|
<groupId>org.springframework.ai</groupId>
|
||||||
<artifactId>spring-ai-stability-ai-spring-boot-starter</artifactId>
|
<artifactId>spring-ai-stability-ai-spring-boot-starter</artifactId>
|
||||||
<version>${spring-ai.version}</version>
|
<version>${spring-ai.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 向量化,基于 Redis 存储,Tika 解析内容 -->
|
|
||||||
|
|
||||||
<!-- 暂不做经济型,先注释 TODO 经济型是啥呀? -->
|
|
||||||
<!-- <dependency>-->
|
|
||||||
<!-- <groupId>${spring-ai.groupId}</groupId>-->
|
|
||||||
<!-- <artifactId>spring-ai-transformers-spring-boot-starter</artifactId>-->
|
|
||||||
<!-- <version>${spring-ai.version}</version>-->
|
|
||||||
<!-- </dependency>-->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${spring-ai.groupId}</groupId>
|
<!-- 通义千问 -->
|
||||||
|
<groupId>com.alibaba.cloud.ai</groupId>
|
||||||
|
<artifactId>spring-ai-alibaba-starter</artifactId>
|
||||||
|
<version>${spring-ai.version}.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<!-- 文心一言 -->
|
||||||
|
<groupId>org.springframework.ai</groupId>
|
||||||
|
<artifactId>spring-ai-qianfan-spring-boot-starter</artifactId>
|
||||||
|
<version>${spring-ai.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<!-- 智谱 GLM -->
|
||||||
|
<groupId>org.springframework.ai</groupId>
|
||||||
|
<artifactId>spring-ai-zhipuai-spring-boot-starter</artifactId>
|
||||||
|
<version>${spring-ai.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.ai</groupId>
|
||||||
|
<artifactId>spring-ai-minimax-spring-boot-starter</artifactId>
|
||||||
|
<version>${spring-ai.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.ai</groupId>
|
||||||
|
<artifactId>spring-ai-moonshot-spring-boot-starter</artifactId>
|
||||||
|
<version>${spring-ai.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 向量存储:https://db-engines.com/en/ranking/vector+dbms -->
|
||||||
|
<dependency>
|
||||||
|
<!-- Qdrant:https://qdrant.tech/ -->
|
||||||
|
<groupId>org.springframework.ai</groupId>
|
||||||
|
<artifactId>spring-ai-qdrant-store</artifactId>
|
||||||
|
<version>${spring-ai.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<!-- Redis:https://redis.io/docs/latest/develop/get-started/vector-database/ -->
|
||||||
|
<groupId>org.springframework.ai</groupId>
|
||||||
|
<artifactId>spring-ai-redis-store</artifactId>
|
||||||
|
<version>${spring-ai.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.iocoder.boot</groupId>
|
||||||
|
<artifactId>yudao-spring-boot-starter-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<!-- Milvus:https://milvus.io/ -->
|
||||||
|
<groupId>org.springframework.ai</groupId>
|
||||||
|
<artifactId>spring-ai-milvus-store</artifactId>
|
||||||
|
<version>${spring-ai.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<!-- Tika:负责内容的解析 -->
|
||||||
|
<groupId>org.springframework.ai</groupId>
|
||||||
<artifactId>spring-ai-tika-document-reader</artifactId>
|
<artifactId>spring-ai-tika-document-reader</artifactId>
|
||||||
<version>${spring-ai.version}</version>
|
<version>${spring-ai.version}</version>
|
||||||
<!-- TODO 芋艿:boot 项目里,不引入 cloud 依赖!!!另外,这样也是为了解决启动报错的问题! -->
|
<!-- TODO 芋艿:boot 项目里,不引入 cloud 依赖!!!另外,这样也是为了解决启动报错的问题! -->
|
||||||
@ -69,41 +116,6 @@
|
|||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>${spring-ai.groupId}</groupId>
|
|
||||||
<artifactId>spring-ai-redis-store</artifactId>
|
|
||||||
<version>${spring-ai.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
|
||||||
<artifactId>yudao-spring-boot-starter-redis</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>cn.iocoder.boot</groupId>
|
|
||||||
<artifactId>yudao-common</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>${spring-ai.groupId}</groupId>
|
|
||||||
<artifactId>spring-ai-qianfan-spring-boot-starter</artifactId>
|
|
||||||
<version>${spring-ai.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- 阿里云 通义千问 -->
|
|
||||||
<!-- TODO 芋艿:等 spring cloud alibaba ai 发布最新的时候,可以替换掉这个依赖,并且删除我们直接 cv 的代码 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.alibaba</groupId>
|
|
||||||
<artifactId>dashscope-sdk-java</artifactId>
|
|
||||||
<version>2.14.0</version>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-simple</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Test 测试相关 -->
|
<!-- Test 测试相关 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -1,24 +1,33 @@
|
|||||||
package cn.iocoder.yudao.framework.ai.config;
|
package cn.iocoder.yudao.framework.ai.config;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
|
import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
|
||||||
import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl;
|
import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl;
|
||||||
import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
|
import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
|
||||||
import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatOptions;
|
import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel;
|
||||||
|
import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel;
|
||||||
import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
|
import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
|
||||||
|
import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
|
||||||
import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
|
import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
|
||||||
import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
|
import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
|
||||||
import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatOptions;
|
|
||||||
import com.alibaba.cloud.ai.tongyi.TongYiAutoConfiguration;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientProperties;
|
||||||
|
import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreProperties;
|
||||||
|
import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties;
|
||||||
|
import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties;
|
||||||
|
import org.springframework.ai.embedding.BatchingStrategy;
|
||||||
|
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
|
||||||
|
import org.springframework.ai.model.tool.ToolCallingManager;
|
||||||
|
import org.springframework.ai.openai.OpenAiChatModel;
|
||||||
|
import org.springframework.ai.openai.OpenAiChatOptions;
|
||||||
|
import org.springframework.ai.openai.api.OpenAiApi;
|
||||||
import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
|
import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
|
||||||
import org.springframework.ai.tokenizer.TokenCountEstimator;
|
import org.springframework.ai.tokenizer.TokenCountEstimator;
|
||||||
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
|
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Import;
|
|
||||||
import org.springframework.context.annotation.Lazy;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 芋道 AI 自动配置
|
* 芋道 AI 自动配置
|
||||||
@ -26,9 +35,12 @@ import org.springframework.context.annotation.Lazy;
|
|||||||
* @author fansili
|
* @author fansili
|
||||||
*/
|
*/
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@EnableConfigurationProperties(YudaoAiProperties.class)
|
@EnableConfigurationProperties({ YudaoAiProperties.class,
|
||||||
|
QdrantVectorStoreProperties.class, // 解析 Qdrant 配置
|
||||||
|
RedisVectorStoreProperties.class, // 解析 Redis 配置
|
||||||
|
MilvusVectorStoreProperties.class, MilvusServiceClientProperties.class // 解析 Milvus 配置
|
||||||
|
})
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Import(TongYiAutoConfiguration.class)
|
|
||||||
public class YudaoAiAutoConfiguration {
|
public class YudaoAiAutoConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ -36,33 +48,148 @@ public class YudaoAiAutoConfiguration {
|
|||||||
return new AiModelFactoryImpl();
|
return new AiModelFactoryImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ========== 各种 AI Client 创建 ==========
|
// ========== 各种 AI Client 创建 ==========
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty(value = "yudao.ai.deepseek.enable", havingValue = "true")
|
@ConditionalOnProperty(value = "yudao.ai.deepseek.enable", havingValue = "true")
|
||||||
public DeepSeekChatModel deepSeekChatModel(YudaoAiProperties yudaoAiProperties) {
|
public DeepSeekChatModel deepSeekChatModel(YudaoAiProperties yudaoAiProperties) {
|
||||||
YudaoAiProperties.DeepSeekProperties properties = yudaoAiProperties.getDeepSeek();
|
YudaoAiProperties.DeepSeekProperties properties = yudaoAiProperties.getDeepseek();
|
||||||
DeepSeekChatOptions options = DeepSeekChatOptions.builder()
|
return buildDeepSeekChatModel(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeepSeekChatModel buildDeepSeekChatModel(YudaoAiProperties.DeepSeekProperties properties) {
|
||||||
|
if (StrUtil.isEmpty(properties.getModel())) {
|
||||||
|
properties.setModel(DeepSeekChatModel.MODEL_DEFAULT);
|
||||||
|
}
|
||||||
|
OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
|
||||||
|
.openAiApi(OpenAiApi.builder()
|
||||||
|
.baseUrl(DeepSeekChatModel.BASE_URL)
|
||||||
|
.apiKey(properties.getApiKey())
|
||||||
|
.build())
|
||||||
|
.defaultOptions(OpenAiChatOptions.builder()
|
||||||
.model(properties.getModel())
|
.model(properties.getModel())
|
||||||
.temperature(properties.getTemperature())
|
.temperature(properties.getTemperature())
|
||||||
.maxTokens(properties.getMaxTokens())
|
.maxTokens(properties.getMaxTokens())
|
||||||
.topP(properties.getTopP())
|
.topP(properties.getTopP())
|
||||||
|
.build())
|
||||||
|
.toolCallingManager(getToolCallingManager())
|
||||||
.build();
|
.build();
|
||||||
return new DeepSeekChatModel(properties.getApiKey(), options);
|
return new DeepSeekChatModel(openAiChatModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnProperty(value = "yudao.ai.doubao.enable", havingValue = "true")
|
||||||
|
public DouBaoChatModel douBaoChatClient(YudaoAiProperties yudaoAiProperties) {
|
||||||
|
YudaoAiProperties.DouBaoProperties properties = yudaoAiProperties.getDoubao();
|
||||||
|
return buildDouBaoChatClient(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DouBaoChatModel buildDouBaoChatClient(YudaoAiProperties.DouBaoProperties properties) {
|
||||||
|
if (StrUtil.isEmpty(properties.getModel())) {
|
||||||
|
properties.setModel(DouBaoChatModel.MODEL_DEFAULT);
|
||||||
|
}
|
||||||
|
OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
|
||||||
|
.openAiApi(OpenAiApi.builder()
|
||||||
|
.baseUrl(DouBaoChatModel.BASE_URL)
|
||||||
|
.apiKey(properties.getApiKey())
|
||||||
|
.build())
|
||||||
|
.defaultOptions(OpenAiChatOptions.builder()
|
||||||
|
.model(properties.getModel())
|
||||||
|
.temperature(properties.getTemperature())
|
||||||
|
.maxTokens(properties.getMaxTokens())
|
||||||
|
.topP(properties.getTopP())
|
||||||
|
.build())
|
||||||
|
.toolCallingManager(getToolCallingManager())
|
||||||
|
.build();
|
||||||
|
return new DouBaoChatModel(openAiChatModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnProperty(value = "yudao.ai.siliconflow.enable", havingValue = "true")
|
||||||
|
public SiliconFlowChatModel siliconFlowChatClient(YudaoAiProperties yudaoAiProperties) {
|
||||||
|
YudaoAiProperties.SiliconFlowProperties properties = yudaoAiProperties.getSiliconflow();
|
||||||
|
return buildSiliconFlowChatClient(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SiliconFlowChatModel buildSiliconFlowChatClient(YudaoAiProperties.SiliconFlowProperties properties) {
|
||||||
|
if (StrUtil.isEmpty(properties.getModel())) {
|
||||||
|
properties.setModel(SiliconFlowChatModel.MODEL_DEFAULT);
|
||||||
|
}
|
||||||
|
OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
|
||||||
|
.openAiApi(OpenAiApi.builder()
|
||||||
|
.baseUrl(SiliconFlowChatModel.BASE_URL)
|
||||||
|
.apiKey(properties.getApiKey())
|
||||||
|
.build())
|
||||||
|
.defaultOptions(OpenAiChatOptions.builder()
|
||||||
|
.model(properties.getModel())
|
||||||
|
.temperature(properties.getTemperature())
|
||||||
|
.maxTokens(properties.getMaxTokens())
|
||||||
|
.topP(properties.getTopP())
|
||||||
|
.build())
|
||||||
|
.toolCallingManager(getToolCallingManager())
|
||||||
|
.build();
|
||||||
|
return new SiliconFlowChatModel(openAiChatModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnProperty(value = "yudao.ai.hunyuan.enable", havingValue = "true")
|
||||||
|
public HunYuanChatModel hunYuanChatClient(YudaoAiProperties yudaoAiProperties) {
|
||||||
|
YudaoAiProperties.HunYuanProperties properties = yudaoAiProperties.getHunyuan();
|
||||||
|
return buildHunYuanChatClient(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HunYuanChatModel buildHunYuanChatClient(YudaoAiProperties.HunYuanProperties properties) {
|
||||||
|
if (StrUtil.isEmpty(properties.getModel())) {
|
||||||
|
properties.setModel(HunYuanChatModel.MODEL_DEFAULT);
|
||||||
|
}
|
||||||
|
// 特殊:由于混元大模型不提供 deepseek,而是通过知识引擎,所以需要区分下 URL
|
||||||
|
if (StrUtil.isEmpty(properties.getBaseUrl())) {
|
||||||
|
properties.setBaseUrl(
|
||||||
|
StrUtil.startWithIgnoreCase(properties.getModel(), "deepseek") ? HunYuanChatModel.DEEP_SEEK_BASE_URL
|
||||||
|
: HunYuanChatModel.BASE_URL);
|
||||||
|
}
|
||||||
|
// 创建 OpenAiChatModel、HunYuanChatModel 对象
|
||||||
|
OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
|
||||||
|
.openAiApi(OpenAiApi.builder()
|
||||||
|
.baseUrl(properties.getBaseUrl())
|
||||||
|
.apiKey(properties.getApiKey())
|
||||||
|
.build())
|
||||||
|
.defaultOptions(OpenAiChatOptions.builder()
|
||||||
|
.model(properties.getModel())
|
||||||
|
.temperature(properties.getTemperature())
|
||||||
|
.maxTokens(properties.getMaxTokens())
|
||||||
|
.topP(properties.getTopP())
|
||||||
|
.build())
|
||||||
|
.toolCallingManager(getToolCallingManager())
|
||||||
|
.build();
|
||||||
|
return new HunYuanChatModel(openAiChatModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty(value = "yudao.ai.xinghuo.enable", havingValue = "true")
|
@ConditionalOnProperty(value = "yudao.ai.xinghuo.enable", havingValue = "true")
|
||||||
public XingHuoChatModel xingHuoChatClient(YudaoAiProperties yudaoAiProperties) {
|
public XingHuoChatModel xingHuoChatClient(YudaoAiProperties yudaoAiProperties) {
|
||||||
YudaoAiProperties.XingHuoProperties properties = yudaoAiProperties.getXinghuo();
|
YudaoAiProperties.XingHuoProperties properties = yudaoAiProperties.getXinghuo();
|
||||||
XingHuoChatOptions options = XingHuoChatOptions.builder()
|
return buildXingHuoChatClient(properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public XingHuoChatModel buildXingHuoChatClient(YudaoAiProperties.XingHuoProperties properties) {
|
||||||
|
if (StrUtil.isEmpty(properties.getModel())) {
|
||||||
|
properties.setModel(XingHuoChatModel.MODEL_DEFAULT);
|
||||||
|
}
|
||||||
|
OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
|
||||||
|
.openAiApi(OpenAiApi.builder()
|
||||||
|
.baseUrl(XingHuoChatModel.BASE_URL)
|
||||||
|
.apiKey(properties.getAppKey() + ":" + properties.getSecretKey())
|
||||||
|
.build())
|
||||||
|
.defaultOptions(OpenAiChatOptions.builder()
|
||||||
.model(properties.getModel())
|
.model(properties.getModel())
|
||||||
.temperature(properties.getTemperature())
|
.temperature(properties.getTemperature())
|
||||||
.maxTokens(properties.getMaxTokens())
|
.maxTokens(properties.getMaxTokens())
|
||||||
.topK(properties.getTopK())
|
.topP(properties.getTopP())
|
||||||
|
.build())
|
||||||
|
.toolCallingManager(getToolCallingManager())
|
||||||
.build();
|
.build();
|
||||||
return new XingHuoChatModel(properties.getAppKey(), properties.getSecretKey(), options);
|
return new XingHuoChatModel(openAiChatModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ -78,44 +205,20 @@ public class YudaoAiAutoConfiguration {
|
|||||||
return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl());
|
return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== rag 相关 ==========
|
// ========== RAG 相关 ==========
|
||||||
// TODO @xin 免费版本
|
|
||||||
// @Bean
|
|
||||||
// @Lazy // TODO 芋艿:临时注释,避免无法启动」
|
|
||||||
// public TransformersEmbeddingModel transformersEmbeddingClient() {
|
|
||||||
// return new TransformersEmbeddingModel(MetadataMode.EMBED);
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO @xin 默认版本先不弄,目前都先取对应的 EmbeddingModel
|
|
||||||
*/
|
|
||||||
// @Bean
|
|
||||||
// @Lazy // TODO 芋艿:临时注释,避免无法启动
|
|
||||||
// public RedisVectorStore vectorStore(TransformersEmbeddingModel embeddingModel, RedisVectorStoreProperties properties,
|
|
||||||
// RedisProperties redisProperties) {
|
|
||||||
// var config = RedisVectorStore.RedisVectorStoreConfig.builder()
|
|
||||||
// .withIndexName(properties.getIndex())
|
|
||||||
// .withPrefix(properties.getPrefix())
|
|
||||||
// .withMetadataFields(new RedisVectorStore.MetadataField("knowledgeId", Schema.FieldType.NUMERIC))
|
|
||||||
// .build();
|
|
||||||
//
|
|
||||||
// RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel,
|
|
||||||
// new JedisPooled(redisProperties.getHost(), redisProperties.getPort()),
|
|
||||||
// properties.isInitializeSchema());
|
|
||||||
// redisVectorStore.afterPropertiesSet();
|
|
||||||
// return redisVectorStore;
|
|
||||||
// }
|
|
||||||
@Bean
|
|
||||||
@Lazy // TODO 芋艿:临时注释,避免无法启动
|
|
||||||
public TokenTextSplitter tokenTextSplitter() {
|
|
||||||
//TODO @xin 配置提取
|
|
||||||
return new TokenTextSplitter(500, 100, 5, 10000, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Lazy // TODO 芋艿:临时注释,避免无法启动
|
|
||||||
public TokenCountEstimator tokenCountEstimator() {
|
public TokenCountEstimator tokenCountEstimator() {
|
||||||
return new JTokkitTokenCountEstimator();
|
return new JTokkitTokenCountEstimator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public BatchingStrategy batchingStrategy() {
|
||||||
|
return new TokenCountBatchingStrategy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ToolCallingManager getToolCallingManager() {
|
||||||
|
return SpringUtil.getBean(ToolCallingManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user