Commit a8d7da329f35bbd410fa3ea793cea8482467fe9f
Committed by
Gitee
!1256 Spring AI 1.0.0 M6 适配:增加知识库、工具调用(function calling)、工作流、豆包/混元/硅基流动等模型的接入
Merge pull request !1256 from 芋道源码/feature/ai
Showing
180 changed files
with
4714 additions
and
7044 deletions
Too many changes.
To preserve performance only 100 of 180 files are displayed.
.image/common/ai-feature.png
sql/mysql/ruoyi-vue-pro.sql
| @@ -11,7 +11,7 @@ | @@ -11,7 +11,7 @@ | ||
| 11 | Target Server Version : 80200 (8.2.0) | 11 | Target Server Version : 80200 (8.2.0) |
| 12 | File Encoding : 65001 | 12 | File Encoding : 65001 |
| 13 | 13 | ||
| 14 | - Date: 31/12/2024 09:16:18 | 14 | + Date: 14/03/2025 22:52:31 |
| 15 | */ | 15 | */ |
| 16 | 16 | ||
| 17 | SET NAMES utf8mb4; | 17 | SET NAMES utf8mb4; |
| @@ -91,7 +91,7 @@ CREATE TABLE `infra_api_error_log` ( | @@ -91,7 +91,7 @@ CREATE TABLE `infra_api_error_log` ( | ||
| 91 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | 91 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 92 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', | 92 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', |
| 93 | PRIMARY KEY (`id`) USING BTREE | 93 | PRIMARY KEY (`id`) USING BTREE |
| 94 | -) ENGINE = InnoDB AUTO_INCREMENT = 21226 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志'; | 94 | +) ENGINE = InnoDB AUTO_INCREMENT = 21417 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志'; |
| 95 | 95 | ||
| 96 | -- ---------------------------- | 96 | -- ---------------------------- |
| 97 | -- Records of infra_api_error_log | 97 | -- Records of infra_api_error_log |
| @@ -128,7 +128,7 @@ CREATE TABLE `infra_codegen_column` ( | @@ -128,7 +128,7 @@ CREATE TABLE `infra_codegen_column` ( | ||
| 128 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | 128 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 129 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | 129 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 130 | PRIMARY KEY (`id`) USING BTREE | 130 | PRIMARY KEY (`id`) USING BTREE |
| 131 | -) ENGINE = InnoDB AUTO_INCREMENT = 2483 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义'; | 131 | +) ENGINE = InnoDB AUTO_INCREMENT = 2538 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义'; |
| 132 | 132 | ||
| 133 | -- ---------------------------- | 133 | -- ---------------------------- |
| 134 | -- Records of infra_codegen_column | 134 | -- Records of infra_codegen_column |
| @@ -166,7 +166,7 @@ CREATE TABLE `infra_codegen_table` ( | @@ -166,7 +166,7 @@ CREATE TABLE `infra_codegen_table` ( | ||
| 166 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | 166 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 167 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | 167 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 168 | PRIMARY KEY (`id`) USING BTREE | 168 | PRIMARY KEY (`id`) USING BTREE |
| 169 | -) ENGINE = InnoDB AUTO_INCREMENT = 187 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义'; | 169 | +) ENGINE = InnoDB AUTO_INCREMENT = 191 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义'; |
| 170 | 170 | ||
| 171 | -- ---------------------------- | 171 | -- ---------------------------- |
| 172 | -- Records of infra_codegen_table | 172 | -- Records of infra_codegen_table |
| @@ -250,7 +250,7 @@ CREATE TABLE `infra_file` ( | @@ -250,7 +250,7 @@ CREATE TABLE `infra_file` ( | ||
| 250 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | 250 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 251 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | 251 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 252 | PRIMARY KEY (`id`) USING BTREE | 252 | PRIMARY KEY (`id`) USING BTREE |
| 253 | -) ENGINE = InnoDB AUTO_INCREMENT = 1577 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表'; | 253 | +) ENGINE = InnoDB AUTO_INCREMENT = 1655 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表'; |
| 254 | 254 | ||
| 255 | -- ---------------------------- | 255 | -- ---------------------------- |
| 256 | -- Records of infra_file | 256 | -- Records of infra_file |
| @@ -444,7 +444,7 @@ CREATE TABLE `system_dict_data` ( | @@ -444,7 +444,7 @@ CREATE TABLE `system_dict_data` ( | ||
| 444 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | 444 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 445 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | 445 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 446 | PRIMARY KEY (`id`) USING BTREE | 446 | PRIMARY KEY (`id`) USING BTREE |
| 447 | -) ENGINE = InnoDB AUTO_INCREMENT = 1683 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表'; | 447 | +) ENGINE = InnoDB AUTO_INCREMENT = 1694 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表'; |
| 448 | 448 | ||
| 449 | -- ---------------------------- | 449 | -- ---------------------------- |
| 450 | -- Records of system_dict_data | 450 | -- Records of system_dict_data |
| @@ -831,7 +831,7 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st | @@ -831,7 +831,7 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st | ||
| 831 | 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'); | 831 | 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'); |
| 832 | 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'); | 832 | 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'); |
| 833 | 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'); | 833 | 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'); |
| 834 | -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'); | 834 | +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'); |
| 835 | 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'); | 835 | 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'); |
| 836 | 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'); | 836 | 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'); |
| 837 | 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'); | 837 | 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 | @@ -888,13 +888,23 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st | ||
| 888 | 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'); | 888 | 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'); |
| 889 | 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'); | 889 | 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'); |
| 890 | 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'); | 890 | 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'); |
| 891 | -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'); | ||
| 892 | -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'); | ||
| 893 | -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'); | ||
| 894 | -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'); | 891 | +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'); |
| 892 | +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'); | ||
| 893 | +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'); | ||
| 895 | 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'); | 894 | 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'); |
| 896 | 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'); | 895 | 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'); |
| 897 | 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'); | 896 | 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'); |
| 897 | +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'); | ||
| 898 | +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'); | ||
| 899 | +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'); | ||
| 900 | +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'); | ||
| 901 | +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'); | ||
| 902 | +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'); | ||
| 903 | +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'); | ||
| 904 | +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'); | ||
| 905 | +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'); | ||
| 906 | +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'); | ||
| 907 | +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'); | ||
| 898 | COMMIT; | 908 | COMMIT; |
| 899 | 909 | ||
| 900 | -- ---------------------------- | 910 | -- ---------------------------- |
| @@ -914,7 +924,7 @@ CREATE TABLE `system_dict_type` ( | @@ -914,7 +924,7 @@ CREATE TABLE `system_dict_type` ( | ||
| 914 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | 924 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 915 | `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间', | 925 | `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间', |
| 916 | PRIMARY KEY (`id`) USING BTREE | 926 | PRIMARY KEY (`id`) USING BTREE |
| 917 | -) ENGINE = InnoDB AUTO_INCREMENT = 640 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表'; | 927 | +) ENGINE = InnoDB AUTO_INCREMENT = 641 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表'; |
| 918 | 928 | ||
| 919 | -- ---------------------------- | 929 | -- ---------------------------- |
| 920 | -- Records of system_dict_type | 930 | -- Records of system_dict_type |
| @@ -1014,13 +1024,14 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat | @@ -1014,13 +1024,14 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat | ||
| 1014 | 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'); | 1024 | 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'); |
| 1015 | 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'); | 1025 | 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'); |
| 1016 | 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'); | 1026 | 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'); |
| 1017 | -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'); | 1027 | +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'); |
| 1018 | 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'); | 1028 | 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'); |
| 1019 | 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'); | 1029 | 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'); |
| 1020 | 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'); | 1030 | 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'); |
| 1021 | 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'); | 1031 | 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'); |
| 1022 | 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'); | 1032 | 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'); |
| 1023 | 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'); | 1033 | 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'); |
| 1034 | +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'); | ||
| 1024 | COMMIT; | 1035 | COMMIT; |
| 1025 | 1036 | ||
| 1026 | -- ---------------------------- | 1037 | -- ---------------------------- |
| @@ -1044,7 +1055,7 @@ CREATE TABLE `system_login_log` ( | @@ -1044,7 +1055,7 @@ CREATE TABLE `system_login_log` ( | ||
| 1044 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | 1055 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 1045 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', | 1056 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', |
| 1046 | PRIMARY KEY (`id`) USING BTREE | 1057 | PRIMARY KEY (`id`) USING BTREE |
| 1047 | -) ENGINE = InnoDB AUTO_INCREMENT = 3415 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录'; | 1058 | +) ENGINE = InnoDB AUTO_INCREMENT = 3442 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录'; |
| 1048 | 1059 | ||
| 1049 | -- ---------------------------- | 1060 | -- ---------------------------- |
| 1050 | -- Records of system_login_log | 1061 | -- Records of system_login_log |
| @@ -1175,7 +1186,7 @@ CREATE TABLE `system_menu` ( | @@ -1175,7 +1186,7 @@ CREATE TABLE `system_menu` ( | ||
| 1175 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | 1186 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 1176 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | 1187 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 1177 | PRIMARY KEY (`id`) USING BTREE | 1188 | PRIMARY KEY (`id`) USING BTREE |
| 1178 | -) ENGINE = InnoDB AUTO_INCREMENT = 2913 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表'; | 1189 | +) ENGINE = InnoDB AUTO_INCREMENT = 2925 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表'; |
| 1179 | 1190 | ||
| 1180 | -- ---------------------------- | 1191 | -- ---------------------------- |
| 1181 | -- Records of system_menu | 1192 | -- Records of system_menu |
| @@ -1373,7 +1384,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i | @@ -1373,7 +1384,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i | ||
| 1373 | 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'); | 1384 | 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'); |
| 1374 | 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'); | 1385 | 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'); |
| 1375 | 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'); | 1386 | 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'); |
| 1376 | -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'); | 1387 | +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'); |
| 1377 | 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'); | 1388 | 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'); |
| 1378 | 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'); | 1389 | 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'); |
| 1379 | 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'); | 1390 | 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 | @@ -1978,11 +1989,11 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i | ||
| 1978 | 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'); | 1989 | 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'); |
| 1979 | 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'); | 1990 | 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'); |
| 1980 | 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'); | 1991 | 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'); |
| 1981 | -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'); | ||
| 1982 | -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'); | ||
| 1983 | -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'); | ||
| 1984 | -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'); | ||
| 1985 | -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'); | 1992 | +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'); |
| 1993 | +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'); | ||
| 1994 | +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'); | ||
| 1995 | +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'); | ||
| 1996 | +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'); | ||
| 1986 | 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'); | 1997 | 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'); |
| 1987 | 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'); | 1998 | 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'); |
| 1988 | 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'); | 1999 | 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 | @@ -2008,7 +2019,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i | ||
| 2008 | 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'); | 2019 | 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'); |
| 2009 | 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'); | 2020 | 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'); |
| 2010 | 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'); | 2021 | 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'); |
| 2011 | -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'); | 2022 | +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'); |
| 2012 | 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'); | 2023 | 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'); |
| 2013 | 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'); | 2024 | 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'); |
| 2014 | 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'); | 2025 | 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 | @@ -2045,6 +2056,18 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i | ||
| 2045 | 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'); | 2056 | 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'); |
| 2046 | 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'); | 2057 | 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'); |
| 2047 | 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'); | 2058 | 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'); |
| 2059 | +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'); | ||
| 2060 | +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'); | ||
| 2061 | +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'); | ||
| 2062 | +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'); | ||
| 2063 | +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'); | ||
| 2064 | +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'); | ||
| 2065 | +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'); | ||
| 2066 | +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'); | ||
| 2067 | +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'); | ||
| 2068 | +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'); | ||
| 2069 | +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'); | ||
| 2070 | +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'); | ||
| 2048 | COMMIT; | 2071 | COMMIT; |
| 2049 | 2072 | ||
| 2050 | -- ---------------------------- | 2073 | -- ---------------------------- |
| @@ -2166,7 +2189,7 @@ CREATE TABLE `system_oauth2_access_token` ( | @@ -2166,7 +2189,7 @@ CREATE TABLE `system_oauth2_access_token` ( | ||
| 2166 | PRIMARY KEY (`id`) USING BTREE, | 2189 | PRIMARY KEY (`id`) USING BTREE, |
| 2167 | INDEX `idx_access_token`(`access_token` ASC) USING BTREE, | 2190 | INDEX `idx_access_token`(`access_token` ASC) USING BTREE, |
| 2168 | INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE | 2191 | INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE |
| 2169 | -) ENGINE = InnoDB AUTO_INCREMENT = 12055 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌'; | 2192 | +) ENGINE = InnoDB AUTO_INCREMENT = 13666 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌'; |
| 2170 | 2193 | ||
| 2171 | -- ---------------------------- | 2194 | -- ---------------------------- |
| 2172 | -- Records of system_oauth2_access_token | 2195 | -- Records of system_oauth2_access_token |
| @@ -2288,7 +2311,7 @@ CREATE TABLE `system_oauth2_refresh_token` ( | @@ -2288,7 +2311,7 @@ CREATE TABLE `system_oauth2_refresh_token` ( | ||
| 2288 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | 2311 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 2289 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', | 2312 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', |
| 2290 | PRIMARY KEY (`id`) USING BTREE | 2313 | PRIMARY KEY (`id`) USING BTREE |
| 2291 | -) ENGINE = InnoDB AUTO_INCREMENT = 1711 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌'; | 2314 | +) ENGINE = InnoDB AUTO_INCREMENT = 1732 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌'; |
| 2292 | 2315 | ||
| 2293 | -- ---------------------------- | 2316 | -- ---------------------------- |
| 2294 | -- Records of system_oauth2_refresh_token | 2317 | -- Records of system_oauth2_refresh_token |
| @@ -2322,7 +2345,7 @@ CREATE TABLE `system_operate_log` ( | @@ -2322,7 +2345,7 @@ CREATE TABLE `system_operate_log` ( | ||
| 2322 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | 2345 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 2323 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', | 2346 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', |
| 2324 | PRIMARY KEY (`id`) USING BTREE | 2347 | PRIMARY KEY (`id`) USING BTREE |
| 2325 | -) ENGINE = InnoDB AUTO_INCREMENT = 9064 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本'; | 2348 | +) ENGINE = InnoDB AUTO_INCREMENT = 9065 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本'; |
| 2326 | 2349 | ||
| 2327 | -- ---------------------------- | 2350 | -- ---------------------------- |
| 2328 | -- Records of system_operate_log | 2351 | -- Records of system_operate_log |
| @@ -2783,9 +2806,7 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t | @@ -2783,9 +2806,7 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t | ||
| 2783 | 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); | 2806 | 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); |
| 2784 | 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); | 2807 | 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); |
| 2785 | 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); | 2808 | 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); |
| 2786 | -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); | ||
| 2787 | 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); | 2809 | 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); |
| 2788 | -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); | ||
| 2789 | 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); | 2810 | 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); |
| 2790 | 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); | 2811 | 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); |
| 2791 | 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); | 2812 | 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` ( | @@ -3305,7 +3326,7 @@ CREATE TABLE `system_sms_code` ( | ||
| 3305 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', | 3326 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', |
| 3306 | PRIMARY KEY (`id`) USING BTREE, | 3327 | PRIMARY KEY (`id`) USING BTREE, |
| 3307 | INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号' | 3328 | INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号' |
| 3308 | -) ENGINE = InnoDB AUTO_INCREMENT = 645 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码'; | 3329 | +) ENGINE = InnoDB AUTO_INCREMENT = 646 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码'; |
| 3309 | 3330 | ||
| 3310 | -- ---------------------------- | 3331 | -- ---------------------------- |
| 3311 | -- Records of system_sms_code | 3332 | -- Records of system_sms_code |
| @@ -3346,7 +3367,7 @@ CREATE TABLE `system_sms_log` ( | @@ -3346,7 +3367,7 @@ CREATE TABLE `system_sms_log` ( | ||
| 3346 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | 3367 | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', |
| 3347 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | 3368 | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', |
| 3348 | PRIMARY KEY (`id`) USING BTREE | 3369 | PRIMARY KEY (`id`) USING BTREE |
| 3349 | -) ENGINE = InnoDB AUTO_INCREMENT = 1241 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志'; | 3370 | +) ENGINE = InnoDB AUTO_INCREMENT = 1255 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志'; |
| 3350 | 3371 | ||
| 3351 | -- ---------------------------- | 3372 | -- ---------------------------- |
| 3352 | -- Records of system_sms_log | 3373 | -- Records of system_sms_log |
| @@ -3588,7 +3609,7 @@ CREATE TABLE `system_user_role` ( | @@ -3588,7 +3609,7 @@ CREATE TABLE `system_user_role` ( | ||
| 3588 | `deleted` bit(1) NULL DEFAULT b'0' COMMENT '是否删除', | 3609 | `deleted` bit(1) NULL DEFAULT b'0' COMMENT '是否删除', |
| 3589 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', | 3610 | `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', |
| 3590 | PRIMARY KEY (`id`) USING BTREE | 3611 | PRIMARY KEY (`id`) USING BTREE |
| 3591 | -) ENGINE = InnoDB AUTO_INCREMENT = 47 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户和角色关联表'; | 3612 | +) ENGINE = InnoDB AUTO_INCREMENT = 48 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户和角色关联表'; |
| 3592 | 3613 | ||
| 3593 | -- ---------------------------- | 3614 | -- ---------------------------- |
| 3594 | -- Records of system_user_role | 3615 | -- Records of system_user_role |
| @@ -3604,12 +3625,12 @@ INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_t | @@ -3604,12 +3625,12 @@ INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_t | ||
| 3604 | 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); | 3625 | 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); |
| 3605 | 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); | 3626 | 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); |
| 3606 | 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); | 3627 | 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); |
| 3607 | -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); | ||
| 3608 | 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); | 3628 | 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); |
| 3609 | 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); | 3629 | 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); |
| 3610 | 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); | 3630 | 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); |
| 3611 | 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); | 3631 | 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); |
| 3612 | 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); | 3632 | 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); |
| 3633 | +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); | ||
| 3613 | COMMIT; | 3634 | COMMIT; |
| 3614 | 3635 | ||
| 3615 | -- ---------------------------- | 3636 | -- ---------------------------- |
| @@ -3644,10 +3665,10 @@ CREATE TABLE `system_users` ( | @@ -3644,10 +3665,10 @@ CREATE TABLE `system_users` ( | ||
| 3644 | -- Records of system_users | 3665 | -- Records of system_users |
| 3645 | -- ---------------------------- | 3666 | -- ---------------------------- |
| 3646 | BEGIN; | 3667 | BEGIN; |
| 3647 | -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); | 3668 | +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); |
| 3648 | 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); | 3669 | 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); |
| 3649 | 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); | 3670 | 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); |
| 3650 | -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); | 3671 | +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); |
| 3651 | 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); | 3672 | 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); |
| 3652 | 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); | 3673 | 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); |
| 3653 | 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); | 3674 | 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); |
yudao-dependencies/pom.xml
| @@ -60,7 +60,7 @@ | @@ -60,7 +60,7 @@ | ||
| 60 | <transmittable-thread-local.version>2.14.5</transmittable-thread-local.version> | 60 | <transmittable-thread-local.version>2.14.5</transmittable-thread-local.version> |
| 61 | <commons-net.version>3.11.1</commons-net.version> | 61 | <commons-net.version>3.11.1</commons-net.version> |
| 62 | <jsch.version>0.1.55</jsch.version> | 62 | <jsch.version>0.1.55</jsch.version> |
| 63 | - <tika-core.version>2.9.2</tika-core.version> | 63 | + <tika-core.version>3.1.0</tika-core.version> <!-- TODO 芋艿:升级后,需要看看 jdk8 的兼容性;也要同步 cloud --> |
| 64 | <ip2region.version>2.7.0</ip2region.version> | 64 | <ip2region.version>2.7.0</ip2region.version> |
| 65 | <bizlog-sdk.version>3.0.6</bizlog-sdk.version> | 65 | <bizlog-sdk.version>3.0.6</bizlog-sdk.version> |
| 66 | <netty.version>4.1.116.Final</netty.version> | 66 | <netty.version>4.1.116.Final</netty.version> |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
| @@ -7,6 +7,7 @@ import com.google.common.collect.HashMultimap; | @@ -7,6 +7,7 @@ import com.google.common.collect.HashMultimap; | ||
| 7 | import com.google.common.collect.Multimap; | 7 | import com.google.common.collect.Multimap; |
| 8 | import jakarta.annotation.Resource; | 8 | import jakarta.annotation.Resource; |
| 9 | import jakarta.annotation.security.PermitAll; | 9 | import jakarta.annotation.security.PermitAll; |
| 10 | +import jakarta.servlet.DispatcherType; | ||
| 10 | import org.springframework.boot.autoconfigure.AutoConfiguration; | 11 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 11 | import org.springframework.boot.autoconfigure.AutoConfigureOrder; | 12 | import org.springframework.boot.autoconfigure.AutoConfigureOrder; |
| 12 | import org.springframework.context.ApplicationContext; | 13 | import org.springframework.context.ApplicationContext; |
| @@ -142,7 +143,9 @@ public class YudaoWebSecurityConfigurerAdapter { | @@ -142,7 +143,9 @@ public class YudaoWebSecurityConfigurerAdapter { | ||
| 142 | // ②:每个项目的自定义规则 | 143 | // ②:每个项目的自定义规则 |
| 143 | .authorizeHttpRequests(c -> authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(c))) | 144 | .authorizeHttpRequests(c -> authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(c))) |
| 144 | // ③:兜底规则,必须认证 | 145 | // ③:兜底规则,必须认证 |
| 145 | - .authorizeHttpRequests(c -> c.anyRequest().authenticated()); | 146 | + .authorizeHttpRequests(c -> c |
| 147 | + .dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll() // WebFlux 异步请求,无需认证,目的:SSE 场景 | ||
| 148 | + .anyRequest().authenticated()); | ||
| 146 | 149 | ||
| 147 | // 添加 Token Filter | 150 | // 添加 Token Filter |
| 148 | httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); | 151 | httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); |
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java
| @@ -35,11 +35,7 @@ public enum AiChatRoleEnum { | @@ -35,11 +35,7 @@ public enum AiChatRoleEnum { | ||
| 35 | ### 微信 | 35 | ### 微信 |
| 36 | 除此之外不要任何解释性语句。 | 36 | 除此之外不要任何解释性语句。 |
| 37 | """), | 37 | """), |
| 38 | - | ||
| 39 | - AI_KNOWLEDGE_ROLE("知识库助手", """ | ||
| 40 | - 给你提供一些数据参考:{info},请回答我的问题。 | ||
| 41 | - 请你跟进数据参考与工具返回结果回复用户的请求。 | ||
| 42 | - """); | 38 | + ; |
| 43 | 39 | ||
| 44 | /** | 40 | /** |
| 45 | * 角色名 | 41 | * 角色名 |
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java
| @@ -12,49 +12,53 @@ public interface ErrorCodeConstants { | @@ -12,49 +12,53 @@ public interface ErrorCodeConstants { | ||
| 12 | // ========== API 密钥 1-040-000-000 ========== | 12 | // ========== API 密钥 1-040-000-000 ========== |
| 13 | ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "API 密钥不存在"); | 13 | ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "API 密钥不存在"); |
| 14 | ErrorCode API_KEY_DISABLE = new ErrorCode(1_040_000_001, "API 密钥已禁用!"); | 14 | ErrorCode API_KEY_DISABLE = new ErrorCode(1_040_000_001, "API 密钥已禁用!"); |
| 15 | - ErrorCode API_KEY_MIDJOURNEY_NOT_FOUND = new ErrorCode(1_040_000_900, "Midjourney 模型不存在"); | ||
| 16 | - ErrorCode API_KEY_SUNO_NOT_FOUND = new ErrorCode(1_040_000_901, "Suno 模型不存在"); | ||
| 17 | - ErrorCode API_KEY_IMAGE_NODE_FOUND = new ErrorCode(1_040_000_902, "平台({}) 图片模型未配置"); | ||
| 18 | 15 | ||
| 19 | - // ========== API 聊天模型 1-040-001-000 ========== | ||
| 20 | - ErrorCode CHAT_MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!"); | ||
| 21 | - ErrorCode CHAT_MODEL_DISABLE = new ErrorCode(1_040_001_001, "模型({})已禁用!"); | ||
| 22 | - ErrorCode CHAT_MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认聊天模型"); | 16 | + // ========== API 模型 1-040-001-000 ========== |
| 17 | + ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!"); | ||
| 18 | + ErrorCode MODEL_DISABLE = new ErrorCode(1_040_001_001, "模型({})已禁用!"); | ||
| 19 | + ErrorCode MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认模型"); | ||
| 20 | + ErrorCode MODEL_USE_TYPE_ERROR = new ErrorCode(1_040_001_003, "操作失败,该模型的模型类型不正确"); | ||
| 23 | 21 | ||
| 24 | - // ========== API 聊天模型 1-040-002-000 ========== | 22 | + // ========== API 聊天角色 1-040-002-000 ========== |
| 25 | ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "聊天角色不存在"); | 23 | ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "聊天角色不存在"); |
| 26 | ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, "聊天角色({})已禁用!"); | 24 | ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, "聊天角色({})已禁用!"); |
| 27 | 25 | ||
| 28 | // ========== API 聊天会话 1-040-003-000 ========== | 26 | // ========== API 聊天会话 1-040-003-000 ========== |
| 29 | - | ||
| 30 | ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "对话不存在!"); | 27 | ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "对话不存在!"); |
| 31 | ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, "操作失败,该聊天模型的配置不完整"); | 28 | ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, "操作失败,该聊天模型的配置不完整"); |
| 32 | 29 | ||
| 33 | // ========== API 聊天消息 1-040-004-000 ========== | 30 | // ========== API 聊天消息 1-040-004-000 ========== |
| 34 | - | ||
| 35 | ErrorCode CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, "消息不存在!"); | 31 | ErrorCode CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, "消息不存在!"); |
| 36 | ErrorCode CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "对话生成异常!"); | 32 | ErrorCode CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "对话生成异常!"); |
| 37 | 33 | ||
| 38 | // ========== API 绘画 1-040-005-000 ========== | 34 | // ========== API 绘画 1-040-005-000 ========== |
| 39 | - | ||
| 40 | - ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_022_005_000, "图片不存在!"); | ||
| 41 | - ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_022_005_001, "Midjourney 提交失败!原因:{}"); | ||
| 42 | - ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_022_005_002, "Midjourney 按钮 customId 不存在! {}"); | ||
| 43 | - ErrorCode IMAGE_FAIL = new ErrorCode(1_022_005_002, "图片绘画失败! {}"); | 35 | + ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_040_005_000, "图片不存在!"); |
| 36 | + ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_040_005_001, "Midjourney 提交失败!原因:{}"); | ||
| 37 | + ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_040_005_002, "Midjourney 按钮 customId 不存在! {}"); | ||
| 44 | 38 | ||
| 45 | // ========== API 音乐 1-040-006-000 ========== | 39 | // ========== API 音乐 1-040-006-000 ========== |
| 46 | - ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_022_006_000, "音乐不存在!"); | 40 | + ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_040_006_000, "音乐不存在!"); |
| 47 | 41 | ||
| 48 | - // ========== API 写作 1-022-007-000 ========== | ||
| 49 | - ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_022_007_000, "作文不存在!"); | ||
| 50 | - ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_022_07_001, "写作生成异常!"); | 42 | + // ========== API 写作 1-040-007-000 ========== |
| 43 | + ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_040_007_000, "作文不存在!"); | ||
| 44 | + ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_040_07_001, "写作生成异常!"); | ||
| 51 | 45 | ||
| 52 | // ========== API 思维导图 1-040-008-000 ========== | 46 | // ========== API 思维导图 1-040-008-000 ========== |
| 53 | ErrorCode MIND_MAP_NOT_EXISTS = new ErrorCode(1_040_008_000, "思维导图不存在!"); | 47 | ErrorCode MIND_MAP_NOT_EXISTS = new ErrorCode(1_040_008_000, "思维导图不存在!"); |
| 54 | 48 | ||
| 55 | - // ========== API 知识库 1-022-008-000 ========== | ||
| 56 | - ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_022_008_000, "知识库不存在!"); | ||
| 57 | - ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_022_008_001, "文档不存在!"); | ||
| 58 | - ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_022_008_002, "段落不存在!"); | 49 | + // ========== API 知识库 1-040-009-000 ========== |
| 50 | + ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_040_009_000, "知识库不存在!"); | ||
| 51 | + | ||
| 52 | + ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_040_009_101, "文档不存在!"); | ||
| 53 | + ErrorCode KNOWLEDGE_DOCUMENT_FILE_EMPTY = new ErrorCode(1_040_009_102, "文档内容为空!"); | ||
| 54 | + ErrorCode KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL = new ErrorCode(1_040_009_102, "文件下载失败!"); | ||
| 55 | + ErrorCode KNOWLEDGE_DOCUMENT_FILE_READ_FAIL = new ErrorCode(1_040_009_102, "文档加载失败!"); | ||
| 56 | + | ||
| 57 | + ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_040_009_202, "段落不存在!"); | ||
| 58 | + ErrorCode KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG = new ErrorCode(1_040_009_203, "内容 Token 数为 {},超过最大限制 {}"); | ||
| 59 | + | ||
| 60 | + // ========== AI 工具 1-040-010-000 ========== | ||
| 61 | + ErrorCode TOOL_NOT_EXISTS = new ErrorCode(1_040_010_000, "工具不存在"); | ||
| 62 | + ErrorCode TOOL_NAME_NOT_EXISTS = new ErrorCode(1_040_010_001, "工具({})找不到 Bean"); | ||
| 59 | 63 | ||
| 60 | } | 64 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java
| @@ -12,15 +12,18 @@ import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessage | @@ -12,15 +12,18 @@ import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessage | ||
| 12 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO; | 12 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO; |
| 13 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; | 13 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; |
| 14 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; | 14 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; |
| 15 | +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; | ||
| 16 | +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; | ||
| 15 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | 17 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; |
| 16 | import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService; | 18 | import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService; |
| 17 | import cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService; | 19 | import cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService; |
| 20 | +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; | ||
| 21 | +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; | ||
| 18 | import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; | 22 | import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; |
| 19 | import io.swagger.v3.oas.annotations.Operation; | 23 | import io.swagger.v3.oas.annotations.Operation; |
| 20 | import io.swagger.v3.oas.annotations.Parameter; | 24 | import io.swagger.v3.oas.annotations.Parameter; |
| 21 | import io.swagger.v3.oas.annotations.tags.Tag; | 25 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 22 | import jakarta.annotation.Resource; | 26 | import jakarta.annotation.Resource; |
| 23 | -import jakarta.annotation.security.PermitAll; | ||
| 24 | import jakarta.validation.Valid; | 27 | import jakarta.validation.Valid; |
| 25 | import lombok.extern.slf4j.Slf4j; | 28 | import lombok.extern.slf4j.Slf4j; |
| 26 | import org.springframework.http.MediaType; | 29 | import org.springframework.http.MediaType; |
| @@ -33,7 +36,7 @@ import java.util.List; | @@ -33,7 +36,7 @@ import java.util.List; | ||
| 33 | import java.util.Map; | 36 | import java.util.Map; |
| 34 | 37 | ||
| 35 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 38 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 36 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | 39 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; |
| 37 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | 40 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| 38 | 41 | ||
| 39 | @Tag(name = "管理后台 - 聊天消息") | 42 | @Tag(name = "管理后台 - 聊天消息") |
| @@ -48,6 +51,10 @@ public class AiChatMessageController { | @@ -48,6 +51,10 @@ public class AiChatMessageController { | ||
| 48 | private AiChatConversationService chatConversationService; | 51 | private AiChatConversationService chatConversationService; |
| 49 | @Resource | 52 | @Resource |
| 50 | private AiChatRoleService chatRoleService; | 53 | private AiChatRoleService chatRoleService; |
| 54 | + @Resource | ||
| 55 | + private AiKnowledgeSegmentService knowledgeSegmentService; | ||
| 56 | + @Resource | ||
| 57 | + private AiKnowledgeDocumentService knowledgeDocumentService; | ||
| 51 | 58 | ||
| 52 | @Operation(summary = "发送消息(段式)", description = "一次性返回,响应较慢") | 59 | @Operation(summary = "发送消息(段式)", description = "一次性返回,响应较慢") |
| 53 | @PostMapping("/send") | 60 | @PostMapping("/send") |
| @@ -57,7 +64,6 @@ public class AiChatMessageController { | @@ -57,7 +64,6 @@ public class AiChatMessageController { | ||
| 57 | 64 | ||
| 58 | @Operation(summary = "发送消息(流式)", description = "流式返回,响应较快") | 65 | @Operation(summary = "发送消息(流式)", description = "流式返回,响应较快") |
| 59 | @PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) | 66 | @PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) |
| 60 | - @PermitAll // 解决 SSE 最终响应的时候,会被 Access Denied 拦截的问题 | ||
| 61 | public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) { | 67 | public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) { |
| 62 | return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId()); | 68 | return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId()); |
| 63 | } | 69 | } |
| @@ -71,8 +77,38 @@ public class AiChatMessageController { | @@ -71,8 +77,38 @@ public class AiChatMessageController { | ||
| 71 | if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) { | 77 | if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) { |
| 72 | return success(Collections.emptyList()); | 78 | return success(Collections.emptyList()); |
| 73 | } | 79 | } |
| 80 | + // 1. 获取消息列表 | ||
| 74 | List<AiChatMessageDO> messageList = chatMessageService.getChatMessageListByConversationId(conversationId); | 81 | List<AiChatMessageDO> messageList = chatMessageService.getChatMessageListByConversationId(conversationId); |
| 75 | - return success(BeanUtils.toBean(messageList, AiChatMessageRespVO.class)); | 82 | + if (CollUtil.isEmpty(messageList)) { |
| 83 | + return success(Collections.emptyList()); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + // 2. 拼接数据,主要是知识库段落信息 | ||
| 87 | + Map<Long, AiKnowledgeSegmentDO> segmentMap = knowledgeSegmentService.getKnowledgeSegmentMap(convertListByFlatMap(messageList, | ||
| 88 | + message -> CollUtil.isEmpty(message.getSegmentIds()) ? null : message.getSegmentIds().stream())); | ||
| 89 | + Map<Long, AiKnowledgeDocumentDO> documentMap = knowledgeDocumentService.getKnowledgeDocumentMap( | ||
| 90 | + convertList(segmentMap.values(), AiKnowledgeSegmentDO::getDocumentId)); | ||
| 91 | + List<AiChatMessageRespVO> messageVOList = BeanUtils.toBean(messageList, AiChatMessageRespVO.class); | ||
| 92 | + for (int i = 0; i < messageList.size(); i++) { | ||
| 93 | + AiChatMessageDO message = messageList.get(i); | ||
| 94 | + if (CollUtil.isEmpty(message.getSegmentIds())) { | ||
| 95 | + continue; | ||
| 96 | + } | ||
| 97 | + // 设置知识库段落信息 | ||
| 98 | + messageVOList.get(i).setSegments(convertList(message.getSegmentIds(), segmentId -> { | ||
| 99 | + AiKnowledgeSegmentDO segment = segmentMap.get(segmentId); | ||
| 100 | + if (segment == null) { | ||
| 101 | + return null; | ||
| 102 | + } | ||
| 103 | + AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId()); | ||
| 104 | + if (document == null) { | ||
| 105 | + return null; | ||
| 106 | + } | ||
| 107 | + return new AiChatMessageRespVO.KnowledgeSegment().setId(segment.getId()).setContent(segment.getContent()) | ||
| 108 | + .setDocumentId(segment.getDocumentId()).setDocumentName(document.getName()); | ||
| 109 | + })); | ||
| 110 | + } | ||
| 111 | + return success(messageVOList); | ||
| 76 | } | 112 | } |
| 77 | 113 | ||
| 78 | @Operation(summary = "删除消息") | 114 | @Operation(summary = "删除消息") |
| @@ -105,7 +141,8 @@ public class AiChatMessageController { | @@ -105,7 +141,8 @@ public class AiChatMessageController { | ||
| 105 | Map<Long, AiChatRoleDO> roleMap = chatRoleService.getChatRoleMap( | 141 | Map<Long, AiChatRoleDO> roleMap = chatRoleService.getChatRoleMap( |
| 106 | convertSet(pageResult.getList(), AiChatMessageDO::getRoleId)); | 142 | convertSet(pageResult.getList(), AiChatMessageDO::getRoleId)); |
| 107 | return success(BeanUtils.toBean(pageResult, AiChatMessageRespVO.class, | 143 | return success(BeanUtils.toBean(pageResult, AiChatMessageRespVO.class, |
| 108 | - respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(), role -> respVO.setRoleName(role.getName())))); | 144 | + respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(), |
| 145 | + role -> respVO.setRoleName(role.getName())))); | ||
| 109 | } | 146 | } |
| 110 | 147 | ||
| 111 | @Operation(summary = "管理员删除消息") | 148 | @Operation(summary = "管理员删除消息") |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | 3 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; |
| 4 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | 4 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; |
| 5 | import com.fhs.core.trans.anno.Trans; | 5 | import com.fhs.core.trans.anno.Trans; |
| 6 | import com.fhs.core.trans.constant.TransType; | 6 | import com.fhs.core.trans.constant.TransType; |
| @@ -31,7 +31,7 @@ public class AiChatConversationRespVO implements VO { | @@ -31,7 +31,7 @@ public class AiChatConversationRespVO implements VO { | ||
| 31 | private Long roleId; | 31 | private Long roleId; |
| 32 | 32 | ||
| 33 | @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | 33 | @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 34 | - @Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = "name", ref = "modelName") | 34 | + @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = "name", ref = "modelName") |
| 35 | private Long modelId; | 35 | private Long modelId; |
| 36 | 36 | ||
| 37 | @Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922") | 37 | @Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922") |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java
| @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; | @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | 5 | ||
| 6 | import java.time.LocalDateTime; | 6 | import java.time.LocalDateTime; |
| 7 | +import java.util.List; | ||
| 7 | 8 | ||
| 8 | @Schema(description = "管理后台 - AI 聊天消息 Response VO") | 9 | @Schema(description = "管理后台 - AI 聊天消息 Response VO") |
| 9 | @Data | 10 | @Data |
| @@ -39,6 +40,12 @@ public class AiChatMessageRespVO { | @@ -39,6 +40,12 @@ public class AiChatMessageRespVO { | ||
| 39 | @Schema(description = "是否携带上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | 40 | @Schema(description = "是否携带上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") |
| 40 | private Boolean useContext; | 41 | private Boolean useContext; |
| 41 | 42 | ||
| 43 | + @Schema(description = "知识库段落编号数组", example = "[1,2,3]") | ||
| 44 | + private List<Long> segmentIds; | ||
| 45 | + | ||
| 46 | + @Schema(description = "知识库段落数组") | ||
| 47 | + private List<KnowledgeSegment> segments; | ||
| 48 | + | ||
| 42 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51") | 49 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51") |
| 43 | private LocalDateTime createTime; | 50 | private LocalDateTime createTime; |
| 44 | 51 | ||
| @@ -47,4 +54,22 @@ public class AiChatMessageRespVO { | @@ -47,4 +54,22 @@ public class AiChatMessageRespVO { | ||
| 47 | @Schema(description = "角色名字", example = "小黄") | 54 | @Schema(description = "角色名字", example = "小黄") |
| 48 | private String roleName; | 55 | private String roleName; |
| 49 | 56 | ||
| 57 | + @Schema(description = "知识库段落", example = "Java 开发手册") | ||
| 58 | + @Data | ||
| 59 | + public static class KnowledgeSegment { | ||
| 60 | + | ||
| 61 | + @Schema(description = "段落编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | ||
| 62 | + private Long id; | ||
| 63 | + | ||
| 64 | + @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") | ||
| 65 | + private String content; | ||
| 66 | + | ||
| 67 | + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") | ||
| 68 | + private Long documentId; | ||
| 69 | + | ||
| 70 | + @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品使用手册") | ||
| 71 | + private String documentName; | ||
| 72 | + | ||
| 73 | + } | ||
| 74 | + | ||
| 50 | } | 75 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java
| @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; | @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | 5 | ||
| 6 | import java.time.LocalDateTime; | 6 | import java.time.LocalDateTime; |
| 7 | +import java.util.List; | ||
| 7 | 8 | ||
| 8 | @Schema(description = "管理后台 - AI 聊天消息发送 Response VO") | 9 | @Schema(description = "管理后台 - AI 聊天消息发送 Response VO") |
| 9 | @Data | 10 | @Data |
| @@ -28,6 +29,12 @@ public class AiChatMessageSendRespVO { | @@ -28,6 +29,12 @@ public class AiChatMessageSendRespVO { | ||
| 28 | @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊") | 29 | @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊") |
| 29 | private String content; | 30 | private String content; |
| 30 | 31 | ||
| 32 | + @Schema(description = "知识库段落编号数组", example = "[1,2,3]") | ||
| 33 | + private List<Long> segmentIds; | ||
| 34 | + | ||
| 35 | + @Schema(description = "知识库段落数组") | ||
| 36 | + private List<AiChatMessageRespVO.KnowledgeSegment> segments; | ||
| 37 | + | ||
| 31 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | 38 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 32 | private LocalDateTime createTime; | 39 | private LocalDateTime createTime; |
| 33 | 40 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java
| @@ -14,18 +14,15 @@ import java.util.Map; | @@ -14,18 +14,15 @@ import java.util.Map; | ||
| 14 | @Data | 14 | @Data |
| 15 | public class AiImageDrawReqVO { | 15 | public class AiImageDrawReqVO { |
| 16 | 16 | ||
| 17 | - @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI") | ||
| 18 | - private String platform; // 参见 AiPlatformEnum 枚举 | 17 | + @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
| 18 | + @NotNull(message = "模型编号不能为空") | ||
| 19 | + private Long modelId; | ||
| 19 | 20 | ||
| 20 | @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "画一个长城") | 21 | @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "画一个长城") |
| 21 | @NotEmpty(message = "提示词不能为空") | 22 | @NotEmpty(message = "提示词不能为空") |
| 22 | @Size(max = 1200, message = "提示词最大 1200") | 23 | @Size(max = 1200, message = "提示词最大 1200") |
| 23 | private String prompt; | 24 | private String prompt; |
| 24 | 25 | ||
| 25 | - @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "stable-diffusion-v1-6") | ||
| 26 | - @NotEmpty(message = "模型不能为空") | ||
| 27 | - private String model; | ||
| 28 | - | ||
| 29 | /** | 26 | /** |
| 30 | * 1. dall-e-2 模型:256x256、512x512、1024x1024 | 27 | * 1. dall-e-2 模型:256x256、512x512、1024x1024 |
| 31 | * 2. dall-e-3 模型:1024x1024, 1792x1024, 或 1024x1792 | 28 | * 2. dall-e-3 模型:1024x1024, 1792x1024, 或 1024x1792 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java
| @@ -13,9 +13,9 @@ public class AiMidjourneyImagineReqVO { | @@ -13,9 +13,9 @@ public class AiMidjourneyImagineReqVO { | ||
| 13 | @NotEmpty(message = "提示词不能为空!") | 13 | @NotEmpty(message = "提示词不能为空!") |
| 14 | private String prompt; | 14 | private String prompt; |
| 15 | 15 | ||
| 16 | - @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "midjourney") | ||
| 17 | - @NotEmpty(message = "模型不能为空") | ||
| 18 | - private String model; // 参考 MidjourneyApi.ModelEnum | 16 | + @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 17 | + @NotNull(message = "模型编号不能为空") | ||
| 18 | + private Long modelId; | ||
| 19 | 19 | ||
| 20 | @Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | 20 | @Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 21 | @NotNull(message = "图片宽度不能为空") | 21 | @NotNull(message = "图片宽度不能为空") |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.http
0 → 100644
| 1 | +### 创建知识库 | ||
| 2 | +POST {{baseUrl}}/ai/knowledge/create | ||
| 3 | +Content-Type: application/json | ||
| 4 | +Authorization: {{token}} | ||
| 5 | +tenant-id: {{adminTenantId}} | ||
| 6 | + | ||
| 7 | +{ | ||
| 8 | + "name": "测试标题", | ||
| 9 | + "description": "测试描述", | ||
| 10 | + "embeddingModelId": 30, | ||
| 11 | + "topK": 3, | ||
| 12 | + "similarityThreshold": 0.5, | ||
| 13 | + "status": 0 | ||
| 14 | +} | ||
| 15 | + | ||
| 16 | +### 更新知识库 | ||
| 17 | +PUT {{baseUrl}}/ai/knowledge/update | ||
| 18 | +Content-Type: application/json | ||
| 19 | +Authorization: {{token}} | ||
| 20 | +tenant-id: {{adminTenantId}} | ||
| 21 | + | ||
| 22 | +{ | ||
| 23 | + "id": 1, | ||
| 24 | + "name": "测试标题(更新)", | ||
| 25 | + "description": "测试描述", | ||
| 26 | + "embeddingModelId": 30, | ||
| 27 | + "topK": 5, | ||
| 28 | + "similarityThreshold": 0.6, | ||
| 29 | + "status": 0 | ||
| 30 | +} | ||
| 31 | + | ||
| 32 | +### 获取知识库分页 | ||
| 33 | +GET {{baseUrl}}/ai/knowledge/page?pageNo=1&pageSize=10 | ||
| 34 | +Authorization: {{token}} | ||
| 35 | +tenant-id: {{adminTenantId}} | ||
| 0 | \ No newline at end of file | 36 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 6 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 6 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO; | ||
| 7 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; | 7 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; |
| 8 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeRespVO; | 8 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeRespVO; |
| 9 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO; | 9 | +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO; |
| 10 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; | 10 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; |
| 11 | import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService; | 11 | import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService; |
| 12 | import io.swagger.v3.oas.annotations.Operation; | 12 | import io.swagger.v3.oas.annotations.Operation; |
| 13 | +import io.swagger.v3.oas.annotations.Parameter; | ||
| 13 | import io.swagger.v3.oas.annotations.tags.Tag; | 14 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 14 | import jakarta.annotation.Resource; | 15 | import jakarta.annotation.Resource; |
| 15 | import jakarta.validation.Valid; | 16 | import jakarta.validation.Valid; |
| 17 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 16 | import org.springframework.validation.annotation.Validated; | 18 | import org.springframework.validation.annotation.Validated; |
| 17 | import org.springframework.web.bind.annotation.*; | 19 | import org.springframework.web.bind.annotation.*; |
| 18 | 20 | ||
| 21 | +import java.util.List; | ||
| 22 | + | ||
| 19 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 23 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 20 | -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | 24 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| 21 | 25 | ||
| 22 | @Tag(name = "管理后台 - AI 知识库") | 26 | @Tag(name = "管理后台 - AI 知识库") |
| 23 | @RestController | 27 | @RestController |
| @@ -30,21 +34,42 @@ public class AiKnowledgeController { | @@ -30,21 +34,42 @@ public class AiKnowledgeController { | ||
| 30 | 34 | ||
| 31 | @GetMapping("/page") | 35 | @GetMapping("/page") |
| 32 | @Operation(summary = "获取知识库分页") | 36 | @Operation(summary = "获取知识库分页") |
| 37 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") | ||
| 33 | public CommonResult<PageResult<AiKnowledgeRespVO>> getKnowledgePage(@Valid AiKnowledgePageReqVO pageReqVO) { | 38 | public CommonResult<PageResult<AiKnowledgeRespVO>> getKnowledgePage(@Valid AiKnowledgePageReqVO pageReqVO) { |
| 34 | - PageResult<AiKnowledgeDO> pageResult = knowledgeService.getKnowledgePage(getLoginUserId(), pageReqVO); | 39 | + PageResult<AiKnowledgeDO> pageResult = knowledgeService.getKnowledgePage(pageReqVO); |
| 35 | return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class)); | 40 | return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class)); |
| 36 | } | 41 | } |
| 37 | 42 | ||
| 43 | + @GetMapping("/get") | ||
| 44 | + @Operation(summary = "获得知识库") | ||
| 45 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | ||
| 46 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") | ||
| 47 | + public CommonResult<AiKnowledgeRespVO> getKnowledge(@RequestParam("id") Long id) { | ||
| 48 | + AiKnowledgeDO knowledge = knowledgeService.getKnowledge(id); | ||
| 49 | + return success(BeanUtils.toBean(knowledge, AiKnowledgeRespVO.class)); | ||
| 50 | + } | ||
| 51 | + | ||
| 38 | @PostMapping("/create") | 52 | @PostMapping("/create") |
| 39 | @Operation(summary = "创建知识库") | 53 | @Operation(summary = "创建知识库") |
| 40 | - public CommonResult<Long> createKnowledge(@RequestBody @Valid AiKnowledgeCreateReqVO createReqVO) { | ||
| 41 | - return success(knowledgeService.createKnowledge(createReqVO, getLoginUserId())); | 54 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:create')") |
| 55 | + public CommonResult<Long> createKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO createReqVO) { | ||
| 56 | + return success(knowledgeService.createKnowledge(createReqVO)); | ||
| 42 | } | 57 | } |
| 43 | 58 | ||
| 44 | @PutMapping("/update") | 59 | @PutMapping("/update") |
| 45 | @Operation(summary = "更新知识库") | 60 | @Operation(summary = "更新知识库") |
| 46 | - public CommonResult<Boolean> updateKnowledge(@RequestBody @Valid AiKnowledgeUpdateReqVO updateReqVO) { | ||
| 47 | - knowledgeService.updateKnowledge(updateReqVO, getLoginUserId()); | 61 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:update')") |
| 62 | + public CommonResult<Boolean> updateKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO updateReqVO) { | ||
| 63 | + knowledgeService.updateKnowledge(updateReqVO); | ||
| 48 | return success(true); | 64 | return success(true); |
| 49 | } | 65 | } |
| 66 | + | ||
| 67 | + @GetMapping("/simple-list") | ||
| 68 | + @Operation(summary = "获得知识库的精简列表") | ||
| 69 | + public CommonResult<List<AiKnowledgeRespVO>> getKnowledgeSimpleList() { | ||
| 70 | + List<AiKnowledgeDO> list = knowledgeService.getKnowledgeSimpleListByStatus(CommonStatusEnum.ENABLE.getStatus()); | ||
| 71 | + return success(convertList(list, knowledge -> new AiKnowledgeRespVO() | ||
| 72 | + .setId(knowledge.getId()).setName(knowledge.getName()))); | ||
| 73 | + } | ||
| 74 | + | ||
| 50 | } | 75 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http
0 → 100644
| 1 | +### 创建知识文档 | ||
| 2 | +POST {{baseUrl}}/ai/knowledge/document/create | ||
| 3 | +Content-Type: application/json | ||
| 4 | +Authorization: Bearer {{token}} | ||
| 5 | +tenant-id: {{adminTenantId}} | ||
| 6 | + | ||
| 7 | +{ | ||
| 8 | + "knowledgeId": 2, | ||
| 9 | + "name": "测试文档", | ||
| 10 | + "url": "https://static.iocoder.cn/README.md", | ||
| 11 | + "segmentMaxTokens": 800 | ||
| 12 | +} | ||
| 13 | + | ||
| 14 | +### 批量创建知识文档 | ||
| 15 | +POST {{baseUrl}}/ai/knowledge/document/create-list | ||
| 16 | +Content-Type: application/json | ||
| 17 | +Authorization: Bearer {{token}} | ||
| 18 | +tenant-id: {{adminTenantId}} | ||
| 19 | + | ||
| 20 | +{ | ||
| 21 | + "knowledgeId": 1, | ||
| 22 | + "list": [ | ||
| 23 | + { | ||
| 24 | + "name": "测试文档1", | ||
| 25 | + "url": "https://static.iocoder.cn/README.md", | ||
| 26 | + "segmentMaxTokens": 800 | ||
| 27 | + }, | ||
| 28 | + { | ||
| 29 | + "name": "测试文档2", | ||
| 30 | + "url": "https://static.iocoder.cn/README_yudao.md", | ||
| 31 | + "segmentMaxTokens": 400 | ||
| 32 | + } | ||
| 33 | + ] | ||
| 34 | +} | ||
| 35 | + |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java
| @@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge; | @@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 6 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; | ||
| 7 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentRespVO; | ||
| 8 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; | 6 | +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.*; |
| 9 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; | 7 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; |
| 10 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; | 8 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; |
| 11 | import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; | 9 | import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; |
| @@ -13,9 +11,12 @@ import io.swagger.v3.oas.annotations.Operation; | @@ -13,9 +11,12 @@ import io.swagger.v3.oas.annotations.Operation; | ||
| 13 | import io.swagger.v3.oas.annotations.tags.Tag; | 11 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 14 | import jakarta.annotation.Resource; | 12 | import jakarta.annotation.Resource; |
| 15 | import jakarta.validation.Valid; | 13 | import jakarta.validation.Valid; |
| 14 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 16 | import org.springframework.validation.annotation.Validated; | 15 | import org.springframework.validation.annotation.Validated; |
| 17 | import org.springframework.web.bind.annotation.*; | 16 | import org.springframework.web.bind.annotation.*; |
| 18 | 17 | ||
| 18 | +import java.util.List; | ||
| 19 | + | ||
| 19 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 20 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 20 | 21 | ||
| 21 | @Tag(name = "管理后台 - AI 知识库文档") | 22 | @Tag(name = "管理后台 - AI 知识库文档") |
| @@ -27,25 +28,63 @@ public class AiKnowledgeDocumentController { | @@ -27,25 +28,63 @@ public class AiKnowledgeDocumentController { | ||
| 27 | @Resource | 28 | @Resource |
| 28 | private AiKnowledgeDocumentService documentService; | 29 | private AiKnowledgeDocumentService documentService; |
| 29 | 30 | ||
| 30 | - @PostMapping("/create") | ||
| 31 | - @Operation(summary = "新建文档") | ||
| 32 | - public CommonResult<Long> createKnowledgeDocument(@Valid AiKnowledgeDocumentCreateReqVO reqVO) { | ||
| 33 | - Long knowledgeDocumentId = documentService.createKnowledgeDocument(reqVO); | ||
| 34 | - return success(knowledgeDocumentId); | ||
| 35 | - } | ||
| 36 | - | ||
| 37 | @GetMapping("/page") | 31 | @GetMapping("/page") |
| 38 | @Operation(summary = "获取文档分页") | 32 | @Operation(summary = "获取文档分页") |
| 39 | - public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPage(@Valid AiKnowledgeDocumentPageReqVO pageReqVO) { | 33 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") |
| 34 | + public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPage( | ||
| 35 | + @Valid AiKnowledgeDocumentPageReqVO pageReqVO) { | ||
| 40 | PageResult<AiKnowledgeDocumentDO> pageResult = documentService.getKnowledgeDocumentPage(pageReqVO); | 36 | PageResult<AiKnowledgeDocumentDO> pageResult = documentService.getKnowledgeDocumentPage(pageReqVO); |
| 41 | return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class)); | 37 | return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class)); |
| 42 | } | 38 | } |
| 43 | 39 | ||
| 40 | + @GetMapping("/get") | ||
| 41 | + @Operation(summary = "获取文档详情") | ||
| 42 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") | ||
| 43 | + public CommonResult<AiKnowledgeDocumentRespVO> getKnowledgeDocument(@RequestParam("id") Long id) { | ||
| 44 | + AiKnowledgeDocumentDO document = documentService.getKnowledgeDocument(id); | ||
| 45 | + return success(BeanUtils.toBean(document, AiKnowledgeDocumentRespVO.class)); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + @PostMapping("/create") | ||
| 49 | + @Operation(summary = "新建文档(单个)") | ||
| 50 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:create')") | ||
| 51 | + public CommonResult<Long> createKnowledgeDocument(@RequestBody @Valid AiKnowledgeDocumentCreateReqVO reqVO) { | ||
| 52 | + Long id = documentService.createKnowledgeDocument(reqVO); | ||
| 53 | + return success(id); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + @PostMapping("/create-list") | ||
| 57 | + @Operation(summary = "新建文档(多个)") | ||
| 58 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:create')") | ||
| 59 | + public CommonResult<List<Long>> createKnowledgeDocumentList( | ||
| 60 | + @RequestBody @Valid AiKnowledgeDocumentCreateListReqVO reqVO) { | ||
| 61 | + List<Long> ids = documentService.createKnowledgeDocumentList(reqVO); | ||
| 62 | + return success(ids); | ||
| 63 | + } | ||
| 64 | + | ||
| 44 | @PutMapping("/update") | 65 | @PutMapping("/update") |
| 45 | @Operation(summary = "更新文档") | 66 | @Operation(summary = "更新文档") |
| 67 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:update')") | ||
| 46 | public CommonResult<Boolean> updateKnowledgeDocument(@Valid @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) { | 68 | public CommonResult<Boolean> updateKnowledgeDocument(@Valid @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) { |
| 47 | documentService.updateKnowledgeDocument(reqVO); | 69 | documentService.updateKnowledgeDocument(reqVO); |
| 48 | return success(true); | 70 | return success(true); |
| 49 | } | 71 | } |
| 50 | 72 | ||
| 73 | + @PutMapping("/update-status") | ||
| 74 | + @Operation(summary = "更新文档状态") | ||
| 75 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:update')") | ||
| 76 | + public CommonResult<Boolean> updateKnowledgeDocumentStatus( | ||
| 77 | + @Valid @RequestBody AiKnowledgeDocumentUpdateStatusReqVO reqVO) { | ||
| 78 | + documentService.updateKnowledgeDocumentStatus(reqVO); | ||
| 79 | + return success(true); | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + @DeleteMapping("/delete") | ||
| 83 | + @Operation(summary = "删除文档") | ||
| 84 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:delete')") | ||
| 85 | + public CommonResult<Boolean> deleteKnowledgeDocument(@RequestParam("id") Long id) { | ||
| 86 | + documentService.deleteKnowledgeDocument(id); | ||
| 87 | + return success(true); | ||
| 88 | + } | ||
| 89 | + | ||
| 51 | } | 90 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http
0 → 100644
| 1 | +### 切片内容 | ||
| 2 | +GET {{baseUrl}}/ai/knowledge/segment/split?url=https://static.iocoder.cn/README_yudao.md&segmentMaxTokens=800 | ||
| 3 | +Content-Type: application/json | ||
| 4 | +Authorization: Bearer {{token}} | ||
| 5 | +tenant-id: {{adminTenantId}} | ||
| 6 | + | ||
| 7 | +### 搜索段落内容 | ||
| 8 | +GET {{baseUrl}}/ai/knowledge/segment/search?knowledgeId=2&content=如何使用这个产品&topK=5&similarityThreshold=0.1 | ||
| 9 | +Content-Type: application/json | ||
| 10 | +Authorization: Bearer {{token}} | ||
| 11 | +tenant-id: {{adminTenantId}} | ||
| 12 | + | ||
| 13 | +### 获取文档处理列表 | ||
| 14 | +GET {{baseUrl}}/ai/knowledge/segment/get-process-list?documentIds=1,2,3 | ||
| 15 | +Content-Type: application/json | ||
| 16 | +Authorization: Bearer {{token}} | ||
| 17 | +tenant-id: {{adminTenantId}} | ||
| 0 | \ No newline at end of file | 18 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | ||
| 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 7 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 6 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; | ||
| 7 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentRespVO; | ||
| 8 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; | ||
| 9 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; | 8 | +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.*; |
| 9 | +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; | ||
| 10 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; | 10 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; |
| 11 | +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; | ||
| 11 | import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; | 12 | import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; |
| 13 | +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO; | ||
| 14 | +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO; | ||
| 12 | import io.swagger.v3.oas.annotations.Operation; | 15 | import io.swagger.v3.oas.annotations.Operation; |
| 16 | +import io.swagger.v3.oas.annotations.Parameter; | ||
| 17 | +import io.swagger.v3.oas.annotations.Parameters; | ||
| 13 | import io.swagger.v3.oas.annotations.tags.Tag; | 18 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 14 | import jakarta.annotation.Resource; | 19 | import jakarta.annotation.Resource; |
| 15 | import jakarta.validation.Valid; | 20 | import jakarta.validation.Valid; |
| 21 | +import org.hibernate.validator.constraints.URL; | ||
| 22 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 16 | import org.springframework.validation.annotation.Validated; | 23 | import org.springframework.validation.annotation.Validated; |
| 17 | import org.springframework.web.bind.annotation.*; | 24 | import org.springframework.web.bind.annotation.*; |
| 18 | 25 | ||
| 26 | +import java.util.Collections; | ||
| 27 | +import java.util.List; | ||
| 28 | +import java.util.Map; | ||
| 29 | + | ||
| 19 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 30 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 31 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | ||
| 20 | 32 | ||
| 21 | @Tag(name = "管理后台 - AI 知识库段落") | 33 | @Tag(name = "管理后台 - AI 知识库段落") |
| 22 | @RestController | 34 | @RestController |
| @@ -26,26 +38,93 @@ public class AiKnowledgeSegmentController { | @@ -26,26 +38,93 @@ public class AiKnowledgeSegmentController { | ||
| 26 | 38 | ||
| 27 | @Resource | 39 | @Resource |
| 28 | private AiKnowledgeSegmentService segmentService; | 40 | private AiKnowledgeSegmentService segmentService; |
| 41 | + @Resource | ||
| 42 | + private AiKnowledgeDocumentService documentService; | ||
| 43 | + | ||
| 44 | + @GetMapping("/get") | ||
| 45 | + @Operation(summary = "获取段落详情") | ||
| 46 | + @Parameter(name = "id", description = "段落编号", required = true, example = "1024") | ||
| 47 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") | ||
| 48 | + public CommonResult<AiKnowledgeSegmentRespVO> getKnowledgeSegment(@RequestParam("id") Long id) { | ||
| 49 | + AiKnowledgeSegmentDO segment = segmentService.getKnowledgeSegment(id); | ||
| 50 | + return success(BeanUtils.toBean(segment, AiKnowledgeSegmentRespVO.class)); | ||
| 51 | + } | ||
| 29 | 52 | ||
| 30 | @GetMapping("/page") | 53 | @GetMapping("/page") |
| 31 | @Operation(summary = "获取段落分页") | 54 | @Operation(summary = "获取段落分页") |
| 32 | - public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPage(@Valid AiKnowledgeSegmentPageReqVO pageReqVO) { | 55 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") |
| 56 | + public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPage( | ||
| 57 | + @Valid AiKnowledgeSegmentPageReqVO pageReqVO) { | ||
| 33 | PageResult<AiKnowledgeSegmentDO> pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO); | 58 | PageResult<AiKnowledgeSegmentDO> pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO); |
| 34 | return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class)); | 59 | return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class)); |
| 35 | } | 60 | } |
| 36 | 61 | ||
| 62 | + @PostMapping("/create") | ||
| 63 | + @Operation(summary = "创建段落") | ||
| 64 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:create')") | ||
| 65 | + public CommonResult<Long> createKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO createReqVO) { | ||
| 66 | + return success(segmentService.createKnowledgeSegment(createReqVO)); | ||
| 67 | + } | ||
| 68 | + | ||
| 37 | @PutMapping("/update") | 69 | @PutMapping("/update") |
| 38 | @Operation(summary = "更新段落内容") | 70 | @Operation(summary = "更新段落内容") |
| 39 | - public CommonResult<Boolean> updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentUpdateReqVO reqVO) { | 71 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:update')") |
| 72 | + public CommonResult<Boolean> updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO reqVO) { | ||
| 40 | segmentService.updateKnowledgeSegment(reqVO); | 73 | segmentService.updateKnowledgeSegment(reqVO); |
| 41 | return success(true); | 74 | return success(true); |
| 42 | } | 75 | } |
| 43 | 76 | ||
| 44 | @PutMapping("/update-status") | 77 | @PutMapping("/update-status") |
| 45 | @Operation(summary = "启禁用段落内容") | 78 | @Operation(summary = "启禁用段落内容") |
| 46 | - public CommonResult<Boolean> updateKnowledgeSegmentStatus(@Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) { | 79 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:update')") |
| 80 | + public CommonResult<Boolean> updateKnowledgeSegmentStatus( | ||
| 81 | + @Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) { | ||
| 47 | segmentService.updateKnowledgeSegmentStatus(reqVO); | 82 | segmentService.updateKnowledgeSegmentStatus(reqVO); |
| 48 | return success(true); | 83 | return success(true); |
| 49 | } | 84 | } |
| 50 | 85 | ||
| 86 | + @GetMapping("/split") | ||
| 87 | + @Operation(summary = "切片内容") | ||
| 88 | + @Parameters({ | ||
| 89 | + @Parameter(name = "url", description = "文档 URL", required = true), | ||
| 90 | + @Parameter(name = "segmentMaxTokens", description = "分段的最大 Token 数", required = true) | ||
| 91 | + }) | ||
| 92 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") | ||
| 93 | + public CommonResult<List<AiKnowledgeSegmentRespVO>> splitContent( | ||
| 94 | + @RequestParam("url") @URL String url, | ||
| 95 | + @RequestParam(value = "segmentMaxTokens") Integer segmentMaxTokens) { | ||
| 96 | + List<AiKnowledgeSegmentDO> segments = segmentService.splitContent(url, segmentMaxTokens); | ||
| 97 | + return success(BeanUtils.toBean(segments, AiKnowledgeSegmentRespVO.class)); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + @GetMapping("/get-process-list") | ||
| 101 | + @Operation(summary = "获取文档处理列表") | ||
| 102 | + @Parameter(name = "documentIds", description = "文档编号列表", required = true, example = "1,2,3") | ||
| 103 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") | ||
| 104 | + public CommonResult<List<AiKnowledgeSegmentProcessRespVO>> getKnowledgeSegmentProcessList( | ||
| 105 | + @RequestParam("documentIds") List<Long> documentIds) { | ||
| 106 | + List<AiKnowledgeSegmentProcessRespVO> list = segmentService.getKnowledgeSegmentProcessList(documentIds); | ||
| 107 | + return success(list); | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + @GetMapping("/search") | ||
| 111 | + @Operation(summary = "搜索段落内容") | ||
| 112 | + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") | ||
| 113 | + public CommonResult<List<AiKnowledgeSegmentSearchRespVO>> searchKnowledgeSegment( | ||
| 114 | + @Valid AiKnowledgeSegmentSearchReqVO reqVO) { | ||
| 115 | + // 1. 搜索段落 | ||
| 116 | + List<AiKnowledgeSegmentSearchRespBO> segments = segmentService | ||
| 117 | + .searchKnowledgeSegment(BeanUtils.toBean(reqVO, AiKnowledgeSegmentSearchReqBO.class)); | ||
| 118 | + if (CollUtil.isEmpty(segments)) { | ||
| 119 | + return success(Collections.emptyList()); | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + // 2. 拼接 VO | ||
| 123 | + Map<Long, AiKnowledgeDocumentDO> documentMap = documentService.getKnowledgeDocumentMap(convertSet( | ||
| 124 | + segments, AiKnowledgeSegmentSearchRespBO::getDocumentId)); | ||
| 125 | + return success(BeanUtils.toBean(segments, AiKnowledgeSegmentSearchRespVO.class, | ||
| 126 | + segment -> MapUtils.findAndThen(documentMap, segment.getDocumentId(), | ||
| 127 | + document -> segment.setDocumentName(document.getName())))); | ||
| 128 | + } | ||
| 129 | + | ||
| 51 | } | 130 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import jakarta.validation.constraints.NotBlank; | ||
| 5 | +import jakarta.validation.constraints.NotEmpty; | ||
| 6 | +import jakarta.validation.constraints.NotNull; | ||
| 7 | +import lombok.Data; | ||
| 8 | +import org.hibernate.validator.constraints.URL; | ||
| 9 | + | ||
| 10 | +import java.util.List; | ||
| 11 | + | ||
| 12 | +@Schema(description = "管理后台 - AI 知识库文档批量创建 Request VO") | ||
| 13 | +@Data | ||
| 14 | +public class AiKnowledgeDocumentCreateListReqVO { | ||
| 15 | + | ||
| 16 | + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204") | ||
| 17 | + @NotNull(message = "知识库编号不能为空") | ||
| 18 | + private Long knowledgeId; | ||
| 19 | + | ||
| 20 | + @Schema(description = "分段的最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800") | ||
| 21 | + @NotNull(message = "分段的最大 Token 数不能为空") | ||
| 22 | + private Integer segmentMaxTokens; | ||
| 23 | + | ||
| 24 | + @Schema(description = "文档列表", requiredMode = Schema.RequiredMode.REQUIRED) | ||
| 25 | + @NotEmpty(message = "文档列表不能为空") | ||
| 26 | + private List<Document> list; | ||
| 27 | + | ||
| 28 | + @Schema(description = "文档") | ||
| 29 | + @Data | ||
| 30 | + public static class Document { | ||
| 31 | + | ||
| 32 | + @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "三方登陆") | ||
| 33 | + @NotBlank(message = "文档名称不能为空") | ||
| 34 | + private String name; | ||
| 35 | + | ||
| 36 | + @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn") | ||
| 37 | + @URL(message = "文档 URL 格式不正确") | ||
| 38 | + private String url; | ||
| 39 | + | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | +} | ||
| 0 | \ No newline at end of file | 43 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java
| @@ -8,6 +8,9 @@ import lombok.Data; | @@ -8,6 +8,9 @@ import lombok.Data; | ||
| 8 | @Data | 8 | @Data |
| 9 | public class AiKnowledgeDocumentPageReqVO extends PageParam { | 9 | public class AiKnowledgeDocumentPageReqVO extends PageParam { |
| 10 | 10 | ||
| 11 | + @Schema(description = "知识库编号", example = "1") | ||
| 12 | + private Long knowledgeId; | ||
| 13 | + | ||
| 11 | @Schema(description = "文档名称", example = "Java 开发手册") | 14 | @Schema(description = "文档名称", example = "Java 开发手册") |
| 12 | private String name; | 15 | private String name; |
| 13 | 16 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 4 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 5 | import lombok.Data; | 4 | import lombok.Data; |
| 6 | 5 | ||
| 7 | -@Schema(description = "管理后台 - AI 知识库-文档 Response VO") | 6 | +import java.time.LocalDateTime; |
| 7 | + | ||
| 8 | +@Schema(description = "管理后台 - AI 知识库文档 Response VO") | ||
| 8 | @Data | 9 | @Data |
| 9 | -public class AiKnowledgeDocumentRespVO extends PageParam { | 10 | +public class AiKnowledgeDocumentRespVO { |
| 10 | 11 | ||
| 11 | - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") | 12 | + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") |
| 12 | private Long id; | 13 | private Long id; |
| 13 | 14 | ||
| 14 | @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") | 15 | @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") |
| 15 | private Long knowledgeId; | 16 | private Long knowledgeId; |
| 16 | 17 | ||
| 17 | - @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") | 18 | + @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") |
| 18 | private String name; | 19 | private String name; |
| 19 | 20 | ||
| 20 | - @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 是一门面向对象的语言.....") | 21 | + @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn") |
| 22 | + private String url; | ||
| 23 | + | ||
| 24 | + @Schema(description = "文档内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 是一门面向对象的语言.....") | ||
| 21 | private String content; | 25 | private String content; |
| 22 | 26 | ||
| 23 | - @Schema(description = "文档 url", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn") | ||
| 24 | - private String url; | 27 | + @Schema(description = "文档内容长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") |
| 28 | + private Integer contentLength; | ||
| 25 | 29 | ||
| 26 | - @Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | 30 | + @Schema(description = "文档 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
| 27 | private Integer tokens; | 31 | private Integer tokens; |
| 28 | 32 | ||
| 29 | - @Schema(description = "字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008") | ||
| 30 | - private Integer wordCount; | 33 | + @Schema(description = "分片最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "512") |
| 34 | + private Integer segmentMaxTokens; | ||
| 31 | 35 | ||
| 32 | - @Schema(description = "切片状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 33 | - private Integer sliceStatus; | 36 | + @Schema(description = "召回次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") |
| 37 | + private Integer retrievalCount; | ||
| 34 | 38 | ||
| 35 | - @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | 39 | + @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| 36 | private Integer status; | 40 | private Integer status; |
| 37 | 41 | ||
| 42 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | ||
| 43 | + private LocalDateTime createTime; | ||
| 44 | + | ||
| 38 | } | 45 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | -import cn.iocoder.yudao.framework.common.validation.InEnum; | ||
| 5 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 6 | import jakarta.validation.constraints.NotNull; | 4 | import jakarta.validation.constraints.NotNull; |
| 7 | import lombok.Data; | 5 | import lombok.Data; |
| 8 | 6 | ||
| 9 | - | ||
| 10 | -@Schema(description = "管理后台 - AI 更新 知识库-文档 Request VO") | 7 | +@Schema(description = "管理后台 - AI 知识库文档更新 Request VO") |
| 11 | @Data | 8 | @Data |
| 12 | public class AiKnowledgeDocumentUpdateReqVO { | 9 | public class AiKnowledgeDocumentUpdateReqVO { |
| 13 | 10 | ||
| 14 | - | ||
| 15 | @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583") | 11 | @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583") |
| 16 | @NotNull(message = "编号不能为空") | 12 | @NotNull(message = "编号不能为空") |
| 17 | private Long id; | 13 | private Long id; |
| 18 | 14 | ||
| 19 | - @Schema(description = "是否启用", example = "1") | ||
| 20 | - @InEnum(CommonStatusEnum.class) | ||
| 21 | - private Integer status; | ||
| 22 | - | ||
| 23 | @Schema(description = "名称", example = "Java 开发手册") | 15 | @Schema(description = "名称", example = "Java 开发手册") |
| 24 | private String name; | 16 | private String name; |
| 25 | 17 | ||
| 18 | + @Schema(description = "分片最大 Token 数", example = "1000") | ||
| 19 | + private Integer segmentMaxTokens; | ||
| 20 | + | ||
| 26 | } | 21 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | ||
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 6 | +import jakarta.validation.constraints.NotNull; | ||
| 7 | +import lombok.Data; | ||
| 8 | + | ||
| 9 | +@Schema(description = "管理后台 - AI 知识库文档更新状态 Request VO") | ||
| 10 | +@Data | ||
| 11 | +public class AiKnowledgeDocumentUpdateStatusReqVO { | ||
| 12 | + | ||
| 13 | + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583") | ||
| 14 | + @NotNull(message = "编号不能为空") | ||
| 15 | + private Long id; | ||
| 16 | + | ||
| 17 | + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | ||
| 18 | + @NotNull(message = "状态不能为空") | ||
| 19 | + @InEnum(CommonStatusEnum.class) | ||
| 20 | + private Integer status; | ||
| 21 | + | ||
| 22 | +} | ||
| 0 | \ No newline at end of file | 23 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
| @@ -23,24 +23,8 @@ public class AiKnowledgeDocumentCreateReqVO { | @@ -23,24 +23,8 @@ public class AiKnowledgeDocumentCreateReqVO { | ||
| 23 | @URL(message = "文档 URL 格式不正确") | 23 | @URL(message = "文档 URL 格式不正确") |
| 24 | private String url; | 24 | private String url; |
| 25 | 25 | ||
| 26 | - @Schema(description = "每个段落的目标 token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800") | ||
| 27 | - @NotNull(message = "每个段落的目标 token 数不能为空") | ||
| 28 | - private Integer defaultSegmentTokens; | ||
| 29 | - | ||
| 30 | - @Schema(description = "每个段落的最小字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "350") | ||
| 31 | - @NotNull(message = "每个段落的最小字符数不能为空") | ||
| 32 | - private Integer minSegmentWordCount; | ||
| 33 | - | ||
| 34 | - @Schema(description = "丢弃阈值:低于此阈值的段落会被丢弃", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") | ||
| 35 | - @NotNull(message = "丢弃阈值不能为空") | ||
| 36 | - private Integer minChunkLengthToEmbed; | ||
| 37 | - | ||
| 38 | - @Schema(description = "最大段落数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") | ||
| 39 | - @NotNull(message = "最大段落数不能为空") | ||
| 40 | - private Integer maxNumSegments; | ||
| 41 | - | ||
| 42 | - @Schema(description = "分块是否保留分隔符", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") | ||
| 43 | - @NotNull(message = "分块是否保留分隔符不能为空") | ||
| 44 | - private Boolean keepSeparator; | 26 | + @Schema(description = "分段的最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800") |
| 27 | + @NotNull(message = "分段的最大 Token 数不能为空") | ||
| 28 | + private Integer segmentMaxTokens; | ||
| 45 | 29 | ||
| 46 | } | 30 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; | 4 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 5 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | ||
| 4 | import io.swagger.v3.oas.annotations.media.Schema; | 6 | import io.swagger.v3.oas.annotations.media.Schema; |
| 5 | import lombok.Data; | 7 | import lombok.Data; |
| 8 | +import org.springframework.format.annotation.DateTimeFormat; | ||
| 9 | + | ||
| 10 | +import java.time.LocalDateTime; | ||
| 11 | + | ||
| 12 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | ||
| 6 | 13 | ||
| 7 | @Schema(description = "管理后台 - AI 知识库的分页 Request VO") | 14 | @Schema(description = "管理后台 - AI 知识库的分页 Request VO") |
| 8 | @Data | 15 | @Data |
| 9 | public class AiKnowledgePageReqVO extends PageParam { | 16 | public class AiKnowledgePageReqVO extends PageParam { |
| 10 | 17 | ||
| 11 | - @Schema(description = "知识库名称", example = "Java 开发手册") | 18 | + @Schema(description = "知识库名称", example = "芋艿") |
| 12 | private String name; | 19 | private String name; |
| 13 | 20 | ||
| 21 | + @Schema(description = "是否启用", example = "1") | ||
| 22 | + @InEnum(CommonStatusEnum.class) | ||
| 23 | + private Integer status; | ||
| 24 | + | ||
| 25 | + @Schema(description = "创建时间") | ||
| 26 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 27 | + private LocalDateTime[] createTime; | ||
| 28 | + | ||
| 14 | } | 29 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java
| @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | 5 | ||
| 6 | +import java.time.LocalDateTime; | ||
| 6 | 7 | ||
| 7 | @Schema(description = "管理后台 - AI 知识库 Response VO") | 8 | @Schema(description = "管理后台 - AI 知识库 Response VO") |
| 8 | @Data | 9 | @Data |
| @@ -17,10 +18,22 @@ public class AiKnowledgeRespVO { | @@ -17,10 +18,22 @@ public class AiKnowledgeRespVO { | ||
| 17 | @Schema(description = "知识库描述", example = "帮助你快速构建系统") | 18 | @Schema(description = "知识库描述", example = "帮助你快速构建系统") |
| 18 | private String description; | 19 | private String description; |
| 19 | 20 | ||
| 20 | - @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14") | ||
| 21 | - private Long modelId; | 21 | + @Schema(description = "向量模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14") |
| 22 | + private Long embeddingModelId; | ||
| 22 | 23 | ||
| 23 | - @Schema(description = "模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen-72b-chat") | ||
| 24 | - private String model; | 24 | + @Schema(description = "向量模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen-72b-chat") |
| 25 | + private String embeddingModel; | ||
| 26 | + | ||
| 27 | + @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") | ||
| 28 | + private Integer topK; | ||
| 29 | + | ||
| 30 | + @Schema(description = "相似度阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.7") | ||
| 31 | + private Double similarityThreshold; | ||
| 32 | + | ||
| 33 | + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 34 | + private Integer status; | ||
| 35 | + | ||
| 36 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | ||
| 37 | + private LocalDateTime createTime; | ||
| 25 | 38 | ||
| 26 | } | 39 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 5 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | import jakarta.validation.constraints.NotBlank; | 6 | import jakarta.validation.constraints.NotBlank; |
| 5 | import jakarta.validation.constraints.NotNull; | 7 | import jakarta.validation.constraints.NotNull; |
| 6 | import lombok.Data; | 8 | import lombok.Data; |
| 7 | 9 | ||
| 8 | -import java.util.List; | ||
| 9 | - | ||
| 10 | -@Schema(description = "管理后台 - AI 知识库创建 Request VO") | 10 | +@Schema(description = "管理后台 - AI 知识库新增/修改 Request VO") |
| 11 | @Data | 11 | @Data |
| 12 | -public class AiKnowledgeCreateReqVO { | 12 | +public class AiKnowledgeSaveReqVO { |
| 13 | + | ||
| 14 | + @Schema(description = "对话编号", example = "1204") | ||
| 15 | + private Long id; | ||
| 13 | 16 | ||
| 14 | @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南") | 17 | @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南") |
| 15 | @NotBlank(message = "知识库名称不能为空") | 18 | @NotBlank(message = "知识库名称不能为空") |
| @@ -18,19 +21,21 @@ public class AiKnowledgeCreateReqVO { | @@ -18,19 +21,21 @@ public class AiKnowledgeCreateReqVO { | ||
| 18 | @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "存储 ruoyi-vue-pro 操作文档") | 21 | @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "存储 ruoyi-vue-pro 操作文档") |
| 19 | private String description; | 22 | private String description; |
| 20 | 23 | ||
| 21 | - @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3]") | ||
| 22 | - private List<Long> visibilityPermissions; | 24 | + @Schema(description = "向量模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 25 | + @NotNull(message = "向量模型不能为空") | ||
| 26 | + private Long embeddingModelId; | ||
| 23 | 27 | ||
| 24 | - @Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 25 | - @NotNull(message = "嵌入模型不能为空") | ||
| 26 | - private Long modelId; | 28 | + @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") |
| 29 | + @NotNull(message = "topK 不能为空") | ||
| 30 | + private Integer topK; | ||
| 27 | 31 | ||
| 28 | @Schema(description = "相似性阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.5") | 32 | @Schema(description = "相似性阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.5") |
| 29 | @NotNull(message = "相似性阈值不能为空") | 33 | @NotNull(message = "相似性阈值不能为空") |
| 30 | private Double similarityThreshold; | 34 | private Double similarityThreshold; |
| 31 | 35 | ||
| 32 | - @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") | ||
| 33 | - @NotNull(message = "topK 不能为空") | ||
| 34 | - private Integer topK; | 36 | + @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 37 | + @NotNull(message = "是否启用不能为空") | ||
| 38 | + @InEnum(CommonStatusEnum.class) | ||
| 39 | + private Integer status; | ||
| 35 | 40 | ||
| 36 | } | 41 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; | ||
| 2 | - | ||
| 3 | -import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | -import jakarta.validation.constraints.NotBlank; | ||
| 5 | -import jakarta.validation.constraints.NotNull; | ||
| 6 | -import lombok.Data; | ||
| 7 | - | ||
| 8 | -import java.util.List; | ||
| 9 | - | ||
| 10 | -@Schema(description = "管理后台 - AI 知识库更新【我的】 Request VO") | ||
| 11 | -@Data | ||
| 12 | -public class AiKnowledgeUpdateReqVO { | ||
| 13 | - | ||
| 14 | - @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204") | ||
| 15 | - @NotNull(message = "知识库编号不能为空") | ||
| 16 | - private Long id; | ||
| 17 | - | ||
| 18 | - @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "") | ||
| 19 | - @NotBlank(message = "知识库名称不能为空") | ||
| 20 | - private String name; | ||
| 21 | - | ||
| 22 | - @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "") | ||
| 23 | - private String description; | ||
| 24 | - | ||
| 25 | - @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") | ||
| 26 | - private List<Long> visibilityPermissions; | ||
| 27 | - | ||
| 28 | - @Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 29 | - @NotNull(message = "嵌入模型不能为空") | ||
| 30 | - private Long modelId; | ||
| 31 | - | ||
| 32 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; | 4 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 5 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | ||
| 4 | import io.swagger.v3.oas.annotations.media.Schema; | 6 | import io.swagger.v3.oas.annotations.media.Schema; |
| 5 | import lombok.Data; | 7 | import lombok.Data; |
| 6 | 8 | ||
| @@ -8,13 +10,14 @@ import lombok.Data; | @@ -8,13 +10,14 @@ import lombok.Data; | ||
| 8 | @Data | 10 | @Data |
| 9 | public class AiKnowledgeSegmentPageReqVO extends PageParam { | 11 | public class AiKnowledgeSegmentPageReqVO extends PageParam { |
| 10 | 12 | ||
| 11 | - @Schema(description = "分段状态", example = "1") | ||
| 12 | - private Integer status; | ||
| 13 | - | ||
| 14 | @Schema(description = "文档编号", example = "1") | 13 | @Schema(description = "文档编号", example = "1") |
| 15 | private Integer documentId; | 14 | private Integer documentId; |
| 16 | 15 | ||
| 17 | @Schema(description = "分段内容关键字", example = "Java 开发") | 16 | @Schema(description = "分段内容关键字", example = "Java 开发") |
| 18 | - private String keyword; | 17 | + private String content; |
| 18 | + | ||
| 19 | + @Schema(description = "分段状态", example = "1") | ||
| 20 | + @InEnum(CommonStatusEnum.class) | ||
| 21 | + private Integer status; | ||
| 19 | 22 | ||
| 20 | } | 23 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +@Schema(description = "管理后台 - AI 知识库段落向量进度 Response VO") | ||
| 7 | +@Data | ||
| 8 | +public class AiKnowledgeSegmentProcessRespVO { | ||
| 9 | + | ||
| 10 | + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 11 | + private Long documentId; | ||
| 12 | + | ||
| 13 | + @Schema(description = "总段落数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") | ||
| 14 | + private Long count; | ||
| 15 | + | ||
| 16 | + @Schema(description = "已向量化段落数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") | ||
| 17 | + private Long embeddingCount; | ||
| 18 | + | ||
| 19 | +} | ||
| 0 | \ No newline at end of file | 20 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java
| @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; | @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | 5 | ||
| 6 | -@Schema(description = "管理后台 - AI 知识库-文档 Response VO") | 6 | +@Schema(description = "管理后台 - AI 知识库文档分片 Response VO") |
| 7 | @Data | 7 | @Data |
| 8 | public class AiKnowledgeSegmentRespVO { | 8 | public class AiKnowledgeSegmentRespVO { |
| 9 | 9 | ||
| @@ -22,13 +22,19 @@ public class AiKnowledgeSegmentRespVO { | @@ -22,13 +22,19 @@ public class AiKnowledgeSegmentRespVO { | ||
| 22 | @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") | 22 | @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") |
| 23 | private String content; | 23 | private String content; |
| 24 | 24 | ||
| 25 | + @Schema(description = "切片内容长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | ||
| 26 | + private Integer contentLength; | ||
| 27 | + | ||
| 25 | @Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | 28 | @Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
| 26 | private Integer tokens; | 29 | private Integer tokens; |
| 27 | 30 | ||
| 28 | - @Schema(description = "字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008") | ||
| 29 | - private Integer wordCount; | 31 | + @Schema(description = "召回次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") |
| 32 | + private Integer retrievalCount; | ||
| 30 | 33 | ||
| 31 | @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | 34 | @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 32 | private Integer status; | 35 | private Integer status; |
| 33 | 36 | ||
| 37 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | ||
| 38 | + private Long createTime; | ||
| 39 | + | ||
| 34 | } | 40 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; |
| 2 | 2 | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | +import jakarta.validation.constraints.NotEmpty; | ||
| 4 | import lombok.Data; | 5 | import lombok.Data; |
| 5 | 6 | ||
| 6 | - | ||
| 7 | -@Schema(description = "管理后台 - AI 更新 知识库-段落 request VO") | 7 | +@Schema(description = "管理后台 - AI 新增/修改知识库段落 request VO") |
| 8 | @Data | 8 | @Data |
| 9 | -public class AiKnowledgeSegmentUpdateReqVO { | 9 | +public class AiKnowledgeSegmentSaveReqVO { |
| 10 | 10 | ||
| 11 | - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") | 11 | + @Schema(description = "编号", example = "24790") |
| 12 | private Long id; | 12 | private Long id; |
| 13 | 13 | ||
| 14 | + @Schema(description = "知识库文档编号", example = "1024") | ||
| 15 | + private Long documentId; | ||
| 16 | + | ||
| 14 | @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") | 17 | @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") |
| 18 | + @NotEmpty(message = "切片内容不能为空") | ||
| 15 | private String content; | 19 | private String content; |
| 16 | 20 | ||
| 17 | } | 21 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java
| @@ -3,15 +3,25 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; | @@ -3,15 +3,25 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | 5 | ||
| 6 | +import jakarta.validation.constraints.NotEmpty; | ||
| 7 | +import jakarta.validation.constraints.NotNull; | ||
| 6 | 8 | ||
| 7 | -@Schema(description = "管理后台 - AI 知识库段落召回 Request VO") | 9 | +@Schema(description = "管理后台 - AI 知识库段落搜索 Request VO") |
| 8 | @Data | 10 | @Data |
| 9 | public class AiKnowledgeSegmentSearchReqVO { | 11 | public class AiKnowledgeSegmentSearchReqVO { |
| 10 | 12 | ||
| 11 | - @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") | 13 | + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
| 14 | + @NotNull(message = "知识库编号不能为空") | ||
| 12 | private Long knowledgeId; | 15 | private Long knowledgeId; |
| 13 | 16 | ||
| 14 | - @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 学习路线") | 17 | + @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "如何使用这个产品") |
| 18 | + @NotEmpty(message = "内容不能为空") | ||
| 15 | private String content; | 19 | private String content; |
| 16 | 20 | ||
| 21 | + @Schema(description = "最大返回数量", example = "5") | ||
| 22 | + private Integer topK; | ||
| 23 | + | ||
| 24 | + @Schema(description = "相似度阈值", example = "0.7") | ||
| 25 | + private Double similarityThreshold; | ||
| 26 | + | ||
| 17 | } | 27 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +@Schema(description = "管理后台 - AI 知识库段落搜索 Response VO") | ||
| 7 | +@Data | ||
| 8 | +public class AiKnowledgeSegmentSearchRespVO extends AiKnowledgeSegmentRespVO { | ||
| 9 | + | ||
| 10 | + @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品使用手册") | ||
| 11 | + private String documentName; | ||
| 12 | + | ||
| 13 | + @Schema(description = "相似度分数", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.95") | ||
| 14 | + private Double score; | ||
| 15 | + | ||
| 16 | +} | ||
| 0 | \ No newline at end of file | 17 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/AiMindMapController.java
| @@ -12,7 +12,6 @@ import io.swagger.v3.oas.annotations.Operation; | @@ -12,7 +12,6 @@ import io.swagger.v3.oas.annotations.Operation; | ||
| 12 | import io.swagger.v3.oas.annotations.Parameter; | 12 | import io.swagger.v3.oas.annotations.Parameter; |
| 13 | import io.swagger.v3.oas.annotations.tags.Tag; | 13 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 14 | import jakarta.annotation.Resource; | 14 | import jakarta.annotation.Resource; |
| 15 | -import jakarta.annotation.security.PermitAll; | ||
| 16 | import jakarta.validation.Valid; | 15 | import jakarta.validation.Valid; |
| 17 | import org.springframework.http.MediaType; | 16 | import org.springframework.http.MediaType; |
| 18 | import org.springframework.security.access.prepost.PreAuthorize; | 17 | import org.springframework.security.access.prepost.PreAuthorize; |
| @@ -32,7 +31,6 @@ public class AiMindMapController { | @@ -32,7 +31,6 @@ public class AiMindMapController { | ||
| 32 | 31 | ||
| 33 | @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) | 32 | @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) |
| 34 | @Operation(summary = "导图生成(流式)", description = "流式返回,响应较快") | 33 | @Operation(summary = "导图生成(流式)", description = "流式返回,响应较快") |
| 35 | - @PermitAll // 解决 SSE 最终响应的时候,会被 Access Denied 拦截的问题 | ||
| 36 | public Flux<CommonResult<String>> generateMindMap(@RequestBody @Valid AiMindMapGenerateReqVO generateReqVO) { | 34 | public Flux<CommonResult<String>> generateMindMap(@RequestBody @Valid AiMindMapGenerateReqVO generateReqVO) { |
| 37 | return mindMapService.generateMindMap(generateReqVO, getLoginUserId()); | 35 | return mindMapService.generateMindMap(generateReqVO, getLoginUserId()); |
| 38 | } | 36 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapPageReqVO.java
| @@ -13,8 +13,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ | @@ -13,8 +13,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ | ||
| 13 | 13 | ||
| 14 | @Schema(description = "管理后台 - AI 思维导图分页 Request VO") | 14 | @Schema(description = "管理后台 - AI 思维导图分页 Request VO") |
| 15 | @Data | 15 | @Data |
| 16 | -@EqualsAndHashCode(callSuper = true) | ||
| 17 | -@ToString(callSuper = true) | ||
| 18 | public class AiMindMapPageReqVO extends PageParam { | 16 | public class AiMindMapPageReqVO extends PageParam { |
| 19 | 17 | ||
| 20 | @Schema(description = "用户编号", example = "4325") | 18 | @Schema(description = "用户编号", example = "4325") |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java
| @@ -6,9 +6,8 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | @@ -6,9 +6,8 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 6 | import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO; | 6 | import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO; |
| 7 | import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyRespVO; | 7 | import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyRespVO; |
| 8 | import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO; | 8 | import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO; |
| 9 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelRespVO; | 9 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelRespVO; |
| 10 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; | 10 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; |
| 11 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | ||
| 12 | import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; | 11 | import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; |
| 13 | import io.swagger.v3.oas.annotations.Operation; | 12 | import io.swagger.v3.oas.annotations.Operation; |
| 14 | import io.swagger.v3.oas.annotations.Parameter; | 13 | import io.swagger.v3.oas.annotations.Parameter; |
| @@ -76,9 +75,9 @@ public class AiApiKeyController { | @@ -76,9 +75,9 @@ public class AiApiKeyController { | ||
| 76 | 75 | ||
| 77 | @GetMapping("/simple-list") | 76 | @GetMapping("/simple-list") |
| 78 | @Operation(summary = "获得 API 密钥分页列表") | 77 | @Operation(summary = "获得 API 密钥分页列表") |
| 79 | - public CommonResult<List<AiChatModelRespVO>> getApiKeySimpleList() { | 78 | + public CommonResult<List<AiModelRespVO>> getApiKeySimpleList() { |
| 80 | List<AiApiKeyDO> list = apiKeyService.getApiKeyList(); | 79 | List<AiApiKeyDO> list = apiKeyService.getApiKeyList(); |
| 81 | - return success(convertList(list, key -> new AiChatModelRespVO().setId(key.getId()).setName(key.getName()))); | 80 | + return success(convertList(list, key -> new AiModelRespVO().setId(key.getId()).setName(key.getName()))); |
| 82 | } | 81 | } |
| 83 | 82 | ||
| 84 | } | 83 | } |
| 85 | \ No newline at end of file | 84 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.model; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 4 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 5 | -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 6 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO; | ||
| 7 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelRespVO; | ||
| 8 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO; | ||
| 9 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | ||
| 10 | -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; | ||
| 11 | -import io.swagger.v3.oas.annotations.Operation; | ||
| 12 | -import io.swagger.v3.oas.annotations.Parameter; | ||
| 13 | -import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 14 | -import jakarta.annotation.Resource; | ||
| 15 | -import jakarta.validation.Valid; | ||
| 16 | -import org.springframework.security.access.prepost.PreAuthorize; | ||
| 17 | -import org.springframework.validation.annotation.Validated; | ||
| 18 | -import org.springframework.web.bind.annotation.*; | ||
| 19 | - | ||
| 20 | -import java.util.List; | ||
| 21 | - | ||
| 22 | -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||
| 23 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||
| 24 | - | ||
| 25 | -@Tag(name = "管理后台 - AI 聊天模型") | ||
| 26 | -@RestController | ||
| 27 | -@RequestMapping("/ai/chat-model") | ||
| 28 | -@Validated | ||
| 29 | -public class AiChatModelController { | ||
| 30 | - | ||
| 31 | - @Resource | ||
| 32 | - private AiChatModelService chatModelService; | ||
| 33 | - | ||
| 34 | - @PostMapping("/create") | ||
| 35 | - @Operation(summary = "创建聊天模型") | ||
| 36 | - @PreAuthorize("@ss.hasPermission('ai:chat-model:create')") | ||
| 37 | - public CommonResult<Long> createChatModel(@Valid @RequestBody AiChatModelSaveReqVO createReqVO) { | ||
| 38 | - return success(chatModelService.createChatModel(createReqVO)); | ||
| 39 | - } | ||
| 40 | - | ||
| 41 | - @PutMapping("/update") | ||
| 42 | - @Operation(summary = "更新聊天模型") | ||
| 43 | - @PreAuthorize("@ss.hasPermission('ai:chat-model:update')") | ||
| 44 | - public CommonResult<Boolean> updateChatModel(@Valid @RequestBody AiChatModelSaveReqVO updateReqVO) { | ||
| 45 | - chatModelService.updateChatModel(updateReqVO); | ||
| 46 | - return success(true); | ||
| 47 | - } | ||
| 48 | - | ||
| 49 | - @DeleteMapping("/delete") | ||
| 50 | - @Operation(summary = "删除聊天模型") | ||
| 51 | - @Parameter(name = "id", description = "编号", required = true) | ||
| 52 | - @PreAuthorize("@ss.hasPermission('ai:chat-model:delete')") | ||
| 53 | - public CommonResult<Boolean> deleteChatModel(@RequestParam("id") Long id) { | ||
| 54 | - chatModelService.deleteChatModel(id); | ||
| 55 | - return success(true); | ||
| 56 | - } | ||
| 57 | - | ||
| 58 | - @GetMapping("/get") | ||
| 59 | - @Operation(summary = "获得聊天模型") | ||
| 60 | - @Parameter(name = "id", description = "编号", required = true, example = "1024") | ||
| 61 | - @PreAuthorize("@ss.hasPermission('ai:chat-model:query')") | ||
| 62 | - public CommonResult<AiChatModelRespVO> getChatModel(@RequestParam("id") Long id) { | ||
| 63 | - AiChatModelDO chatModel = chatModelService.getChatModel(id); | ||
| 64 | - return success(BeanUtils.toBean(chatModel, AiChatModelRespVO.class)); | ||
| 65 | - } | ||
| 66 | - | ||
| 67 | - @GetMapping("/page") | ||
| 68 | - @Operation(summary = "获得聊天模型分页") | ||
| 69 | - @PreAuthorize("@ss.hasPermission('ai:chat-model:query')") | ||
| 70 | - public CommonResult<PageResult<AiChatModelRespVO>> getChatModelPage(@Valid AiChatModelPageReqVO pageReqVO) { | ||
| 71 | - PageResult<AiChatModelDO> pageResult = chatModelService.getChatModelPage(pageReqVO); | ||
| 72 | - return success(BeanUtils.toBean(pageResult, AiChatModelRespVO.class)); | ||
| 73 | - } | ||
| 74 | - | ||
| 75 | - @GetMapping("/simple-list") | ||
| 76 | - @Operation(summary = "获得聊天模型列表") | ||
| 77 | - @Parameter(name = "status", description = "状态", required = true, example = "1") | ||
| 78 | - public CommonResult<List<AiChatModelRespVO>> getChatModelSimpleList(@RequestParam("status") Integer status) { | ||
| 79 | - List<AiChatModelDO> list = chatModelService.getChatModelListByStatus(status); | ||
| 80 | - return success(convertList(list, model -> new AiChatModelRespVO().setId(model.getId()) | ||
| 81 | - .setName(model.getName()).setModel(model.getModel()))); | ||
| 82 | - } | ||
| 83 | - | ||
| 84 | -} | ||
| 85 | \ No newline at end of file | 0 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java
| @@ -75,7 +75,7 @@ public class AiChatRoleController { | @@ -75,7 +75,7 @@ public class AiChatRoleController { | ||
| 75 | @GetMapping("/category-list") | 75 | @GetMapping("/category-list") |
| 76 | @Operation(summary = "获得聊天角色的分类列表") | 76 | @Operation(summary = "获得聊天角色的分类列表") |
| 77 | public CommonResult<List<String>> getChatRoleCategoryList() { | 77 | public CommonResult<List<String>> getChatRoleCategoryList() { |
| 78 | - return success(chatRoleService.getChatRoleCategoryList()); | 78 | + return success(chatRoleService.getChatRoleCategoryList()); |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | // ========== 角色管理 ========== | 81 | // ========== 角色管理 ========== |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiModelController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.model; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 6 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 7 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO; | ||
| 8 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelRespVO; | ||
| 9 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO; | ||
| 10 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; | ||
| 11 | +import cn.iocoder.yudao.module.ai.service.model.AiModelService; | ||
| 12 | +import io.swagger.v3.oas.annotations.Operation; | ||
| 13 | +import io.swagger.v3.oas.annotations.Parameter; | ||
| 14 | +import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 15 | +import jakarta.annotation.Resource; | ||
| 16 | +import jakarta.validation.Valid; | ||
| 17 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 18 | +import org.springframework.validation.annotation.Validated; | ||
| 19 | +import org.springframework.web.bind.annotation.*; | ||
| 20 | + | ||
| 21 | +import java.util.List; | ||
| 22 | + | ||
| 23 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||
| 24 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||
| 25 | + | ||
| 26 | +@Tag(name = "管理后台 - AI 模型") | ||
| 27 | +@RestController | ||
| 28 | +@RequestMapping("/ai/model") | ||
| 29 | +@Validated | ||
| 30 | +public class AiModelController { | ||
| 31 | + | ||
| 32 | + @Resource | ||
| 33 | + private AiModelService modelService; | ||
| 34 | + | ||
| 35 | + @PostMapping("/create") | ||
| 36 | + @Operation(summary = "创建模型") | ||
| 37 | + @PreAuthorize("@ss.hasPermission('ai:model:create')") | ||
| 38 | + public CommonResult<Long> createModel(@Valid @RequestBody AiModelSaveReqVO createReqVO) { | ||
| 39 | + return success(modelService.createModel(createReqVO)); | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + @PutMapping("/update") | ||
| 43 | + @Operation(summary = "更新模型") | ||
| 44 | + @PreAuthorize("@ss.hasPermission('ai:model:update')") | ||
| 45 | + public CommonResult<Boolean> updateModel(@Valid @RequestBody AiModelSaveReqVO updateReqVO) { | ||
| 46 | + modelService.updateModel(updateReqVO); | ||
| 47 | + return success(true); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + @DeleteMapping("/delete") | ||
| 51 | + @Operation(summary = "删除模型") | ||
| 52 | + @Parameter(name = "id", description = "编号", required = true) | ||
| 53 | + @PreAuthorize("@ss.hasPermission('ai:model:delete')") | ||
| 54 | + public CommonResult<Boolean> deleteModel(@RequestParam("id") Long id) { | ||
| 55 | + modelService.deleteModel(id); | ||
| 56 | + return success(true); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + @GetMapping("/get") | ||
| 60 | + @Operation(summary = "获得模型") | ||
| 61 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | ||
| 62 | + @PreAuthorize("@ss.hasPermission('ai:model:query')") | ||
| 63 | + public CommonResult<AiModelRespVO> getModel(@RequestParam("id") Long id) { | ||
| 64 | + AiModelDO model = modelService.getModel(id); | ||
| 65 | + return success(BeanUtils.toBean(model, AiModelRespVO.class)); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + @GetMapping("/page") | ||
| 69 | + @Operation(summary = "获得模型分页") | ||
| 70 | + @PreAuthorize("@ss.hasPermission('ai:model:query')") | ||
| 71 | + public CommonResult<PageResult<AiModelRespVO>> getModelPage(@Valid AiModelPageReqVO pageReqVO) { | ||
| 72 | + PageResult<AiModelDO> pageResult = modelService.getModelPage(pageReqVO); | ||
| 73 | + return success(BeanUtils.toBean(pageResult, AiModelRespVO.class)); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + @GetMapping("/simple-list") | ||
| 77 | + @Operation(summary = "获得模型列表") | ||
| 78 | + @Parameter(name = "type", description = "类型", required = true, example = "1") | ||
| 79 | + @Parameter(name = "platform", description = "平台", example = "midjourney") | ||
| 80 | + public CommonResult<List<AiModelRespVO>> getModelSimpleList( | ||
| 81 | + @RequestParam("type") Integer type, | ||
| 82 | + @RequestParam(value = "platform", required = false) String platform) { | ||
| 83 | + List<AiModelDO> list = modelService.getModelListByStatusAndType( | ||
| 84 | + CommonStatusEnum.ENABLE.getStatus(), type, platform); | ||
| 85 | + return success(convertList(list, model -> new AiModelRespVO().setId(model.getId()) | ||
| 86 | + .setName(model.getName()).setModel(model.getModel()).setPlatform(model.getPlatform()))); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | +} | ||
| 0 | \ No newline at end of file | 90 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiToolController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.model; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 6 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 7 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO; | ||
| 8 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolRespVO; | ||
| 9 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO; | ||
| 10 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; | ||
| 11 | +import cn.iocoder.yudao.module.ai.service.model.AiToolService; | ||
| 12 | +import io.swagger.v3.oas.annotations.Operation; | ||
| 13 | +import io.swagger.v3.oas.annotations.Parameter; | ||
| 14 | +import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 15 | +import jakarta.annotation.Resource; | ||
| 16 | +import jakarta.validation.Valid; | ||
| 17 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 18 | +import org.springframework.validation.annotation.Validated; | ||
| 19 | +import org.springframework.web.bind.annotation.*; | ||
| 20 | + | ||
| 21 | +import java.util.List; | ||
| 22 | + | ||
| 23 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||
| 24 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||
| 25 | + | ||
| 26 | +@Tag(name = "管理后台 - AI 工具") | ||
| 27 | +@RestController | ||
| 28 | +@RequestMapping("/ai/tool") | ||
| 29 | +@Validated | ||
| 30 | +public class AiToolController { | ||
| 31 | + | ||
| 32 | + @Resource | ||
| 33 | + private AiToolService toolService; | ||
| 34 | + | ||
| 35 | + @PostMapping("/create") | ||
| 36 | + @Operation(summary = "创建工具") | ||
| 37 | + @PreAuthorize("@ss.hasPermission('ai:tool:create')") | ||
| 38 | + public CommonResult<Long> createTool(@Valid @RequestBody AiToolSaveReqVO createReqVO) { | ||
| 39 | + return success(toolService.createTool(createReqVO)); | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + @PutMapping("/update") | ||
| 43 | + @Operation(summary = "更新工具") | ||
| 44 | + @PreAuthorize("@ss.hasPermission('ai:tool:update')") | ||
| 45 | + public CommonResult<Boolean> updateTool(@Valid @RequestBody AiToolSaveReqVO updateReqVO) { | ||
| 46 | + toolService.updateTool(updateReqVO); | ||
| 47 | + return success(true); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + @DeleteMapping("/delete") | ||
| 51 | + @Operation(summary = "删除工具") | ||
| 52 | + @Parameter(name = "id", description = "编号", required = true) | ||
| 53 | + @PreAuthorize("@ss.hasPermission('ai:tool:delete')") | ||
| 54 | + public CommonResult<Boolean> deleteTool(@RequestParam("id") Long id) { | ||
| 55 | + toolService.deleteTool(id); | ||
| 56 | + return success(true); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + @GetMapping("/get") | ||
| 60 | + @Operation(summary = "获得工具") | ||
| 61 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | ||
| 62 | + @PreAuthorize("@ss.hasPermission('ai:tool:query')") | ||
| 63 | + public CommonResult<AiToolRespVO> getTool(@RequestParam("id") Long id) { | ||
| 64 | + AiToolDO tool = toolService.getTool(id); | ||
| 65 | + return success(BeanUtils.toBean(tool, AiToolRespVO.class)); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + @GetMapping("/page") | ||
| 69 | + @Operation(summary = "获得工具分页") | ||
| 70 | + @PreAuthorize("@ss.hasPermission('ai:tool:query')") | ||
| 71 | + public CommonResult<PageResult<AiToolRespVO>> getToolPage(@Valid AiToolPageReqVO pageReqVO) { | ||
| 72 | + PageResult<AiToolDO> pageResult = toolService.getToolPage(pageReqVO); | ||
| 73 | + return success(BeanUtils.toBean(pageResult, AiToolRespVO.class)); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + @GetMapping("/simple-list") | ||
| 77 | + @Operation(summary = "获得工具列表") | ||
| 78 | + public CommonResult<List<AiToolRespVO>> getToolSimpleList() { | ||
| 79 | + List<AiToolDO> list = toolService.getToolListByStatus(CommonStatusEnum.ENABLE.getStatus()); | ||
| 80 | + return success(convertList(list, tool -> new AiToolRespVO() | ||
| 81 | + .setId(tool.getId()).setName(tool.getName()))); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | +} | ||
| 0 | \ No newline at end of file | 85 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | 3 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; |
| 4 | import com.fhs.core.trans.anno.Trans; | 4 | import com.fhs.core.trans.anno.Trans; |
| 5 | import com.fhs.core.trans.constant.TransType; | 5 | import com.fhs.core.trans.constant.TransType; |
| 6 | import com.fhs.core.trans.vo.VO; | 6 | import com.fhs.core.trans.vo.VO; |
| @@ -8,6 +8,7 @@ import io.swagger.v3.oas.annotations.media.Schema; | @@ -8,6 +8,7 @@ import io.swagger.v3.oas.annotations.media.Schema; | ||
| 8 | import lombok.Data; | 8 | import lombok.Data; |
| 9 | 9 | ||
| 10 | import java.time.LocalDateTime; | 10 | import java.time.LocalDateTime; |
| 11 | +import java.util.List; | ||
| 11 | 12 | ||
| 12 | @Schema(description = "管理后台 - AI 聊天角色 Response VO") | 13 | @Schema(description = "管理后台 - AI 聊天角色 Response VO") |
| 13 | @Data | 14 | @Data |
| @@ -20,7 +21,7 @@ public class AiChatRoleRespVO implements VO { | @@ -20,7 +21,7 @@ public class AiChatRoleRespVO implements VO { | ||
| 20 | private Long userId; | 21 | private Long userId; |
| 21 | 22 | ||
| 22 | @Schema(description = "模型编号", example = "17640") | 23 | @Schema(description = "模型编号", example = "17640") |
| 23 | - @Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = {"name", "model"}, refs = {"modelName", "model"}) | 24 | + @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = { "name", "model" }, refs = { "modelName", "model" }) |
| 24 | private Long modelId; | 25 | private Long modelId; |
| 25 | @Schema(description = "模型名字", example = "张三") | 26 | @Schema(description = "模型名字", example = "张三") |
| 26 | private String modelName; | 27 | private String modelName; |
| @@ -45,6 +46,12 @@ public class AiChatRoleRespVO implements VO { | @@ -45,6 +46,12 @@ public class AiChatRoleRespVO implements VO { | ||
| 45 | @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED) | 46 | @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED) |
| 46 | private String systemMessage; | 47 | private String systemMessage; |
| 47 | 48 | ||
| 49 | + @Schema(description = "引用的知识库编号列表", example = "1,2,3") | ||
| 50 | + private List<Long> knowledgeIds; | ||
| 51 | + | ||
| 52 | + @Schema(description = "引用的工具编号列表", example = "1,2,3") | ||
| 53 | + private List<Long> toolIds; | ||
| 54 | + | ||
| 48 | @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | 55 | @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 49 | private Boolean publicStatus; | 56 | private Boolean publicStatus; |
| 50 | 57 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java
| @@ -5,6 +5,8 @@ import jakarta.validation.constraints.NotEmpty; | @@ -5,6 +5,8 @@ import jakarta.validation.constraints.NotEmpty; | ||
| 5 | import lombok.Data; | 5 | import lombok.Data; |
| 6 | import org.hibernate.validator.constraints.URL; | 6 | import org.hibernate.validator.constraints.URL; |
| 7 | 7 | ||
| 8 | +import java.util.List; | ||
| 9 | + | ||
| 8 | @Schema(description = "管理后台 - AI 聊天角色新增/修改【我的】 Request VO") | 10 | @Schema(description = "管理后台 - AI 聊天角色新增/修改【我的】 Request VO") |
| 9 | @Data | 11 | @Data |
| 10 | public class AiChatRoleSaveMyReqVO { | 12 | public class AiChatRoleSaveMyReqVO { |
| @@ -29,4 +31,10 @@ public class AiChatRoleSaveMyReqVO { | @@ -29,4 +31,10 @@ public class AiChatRoleSaveMyReqVO { | ||
| 29 | @NotEmpty(message = "角色设定不能为空") | 31 | @NotEmpty(message = "角色设定不能为空") |
| 30 | private String systemMessage; | 32 | private String systemMessage; |
| 31 | 33 | ||
| 34 | + @Schema(description = "引用的知识库编号列表", example = "1,2,3") | ||
| 35 | + private List<Long> knowledgeIds; | ||
| 36 | + | ||
| 37 | + @Schema(description = "引用的工具编号列表", example = "1,2,3") | ||
| 38 | + private List<Long> toolIds; | ||
| 39 | + | ||
| 32 | } | 40 | } |
| 33 | \ No newline at end of file | 41 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java
| @@ -7,6 +7,8 @@ import lombok.*; | @@ -7,6 +7,8 @@ import lombok.*; | ||
| 7 | import jakarta.validation.constraints.*; | 7 | import jakarta.validation.constraints.*; |
| 8 | import org.hibernate.validator.constraints.URL; | 8 | import org.hibernate.validator.constraints.URL; |
| 9 | 9 | ||
| 10 | +import java.util.List; | ||
| 11 | + | ||
| 10 | @Schema(description = "管理后台 - AI 聊天角色新增/修改 Request VO") | 12 | @Schema(description = "管理后台 - AI 聊天角色新增/修改 Request VO") |
| 11 | @Data | 13 | @Data |
| 12 | public class AiChatRoleSaveReqVO { | 14 | public class AiChatRoleSaveReqVO { |
| @@ -42,6 +44,12 @@ public class AiChatRoleSaveReqVO { | @@ -42,6 +44,12 @@ public class AiChatRoleSaveReqVO { | ||
| 42 | @NotEmpty(message = "角色设定不能为空") | 44 | @NotEmpty(message = "角色设定不能为空") |
| 43 | private String systemMessage; | 45 | private String systemMessage; |
| 44 | 46 | ||
| 47 | + @Schema(description = "引用的知识库编号列表", example = "1,2,3") | ||
| 48 | + private List<Long> knowledgeIds; | ||
| 49 | + | ||
| 50 | + @Schema(description = "引用的工具编号列表", example = "1,2,3") | ||
| 51 | + private List<Long> toolIds; | ||
| 52 | + | ||
| 45 | @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | 53 | @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 46 | @NotNull(message = "是否公开不能为空") | 54 | @NotNull(message = "是否公开不能为空") |
| 47 | private Boolean publicStatus; | 55 | private Boolean publicStatus; |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelPageReqVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel; | 1 | +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model; |
| 2 | 2 | ||
| 3 | import lombok.*; | 3 | import lombok.*; |
| 4 | import io.swagger.v3.oas.annotations.media.Schema; | 4 | import io.swagger.v3.oas.annotations.media.Schema; |
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageParam; | 5 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 6 | 6 | ||
| 7 | -@Schema(description = "管理后台 - API 聊天模型分页 Request VO") | 7 | +@Schema(description = "管理后台 - API 模型分页 Request VO") |
| 8 | @Data | 8 | @Data |
| 9 | -public class AiChatModelPageReqVO extends PageParam { | 9 | +public class AiModelPageReqVO extends PageParam { |
| 10 | 10 | ||
| 11 | @Schema(description = "模型名字", example = "张三") | 11 | @Schema(description = "模型名字", example = "张三") |
| 12 | private String name; | 12 | private String name; |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelRespVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelRespVO.java
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel; | 1 | +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model; |
| 2 | 2 | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | 5 | ||
| 6 | import java.time.LocalDateTime; | 6 | import java.time.LocalDateTime; |
| 7 | 7 | ||
| 8 | -@Schema(description = "管理后台 - AI 聊天模型 Response VO") | 8 | +@Schema(description = "管理后台 - AI 模型 Response VO") |
| 9 | @Data | 9 | @Data |
| 10 | -public class AiChatModelRespVO { | 10 | +public class AiModelRespVO { |
| 11 | 11 | ||
| 12 | @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2630") | 12 | @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2630") |
| 13 | private Long id; | 13 | private Long id; |
| @@ -24,6 +24,9 @@ public class AiChatModelRespVO { | @@ -24,6 +24,9 @@ public class AiChatModelRespVO { | ||
| 24 | @Schema(description = "模型平台", example = "OpenAI") | 24 | @Schema(description = "模型平台", example = "OpenAI") |
| 25 | private String platform; | 25 | private String platform; |
| 26 | 26 | ||
| 27 | + @Schema(description = "模型类型", example = "1") | ||
| 28 | + private Integer type; | ||
| 29 | + | ||
| 27 | @Schema(description = "排序", example = "1") | 30 | @Schema(description = "排序", example = "1") |
| 28 | private Integer sort; | 31 | private Integer sort; |
| 29 | 32 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelSaveReqVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel; | 1 | +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | ||
| 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 5 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 4 | import cn.iocoder.yudao.framework.common.validation.InEnum; | 6 | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| 5 | import io.swagger.v3.oas.annotations.media.Schema; | 7 | import io.swagger.v3.oas.annotations.media.Schema; |
| 6 | -import lombok.*; | ||
| 7 | -import jakarta.validation.constraints.*; | 8 | +import jakarta.validation.constraints.NotEmpty; |
| 9 | +import jakarta.validation.constraints.NotNull; | ||
| 10 | +import lombok.Data; | ||
| 8 | 11 | ||
| 9 | -@Schema(description = "管理后台 - API 聊天模型新增/修改 Request VO") | 12 | +@Schema(description = "管理后台 - API 模型新增/修改 Request VO") |
| 10 | @Data | 13 | @Data |
| 11 | -public class AiChatModelSaveReqVO { | 14 | +public class AiModelSaveReqVO { |
| 12 | 15 | ||
| 13 | @Schema(description = "编号", example = "2630") | 16 | @Schema(description = "编号", example = "2630") |
| 14 | private Long id; | 17 | private Long id; |
| @@ -27,8 +30,14 @@ public class AiChatModelSaveReqVO { | @@ -27,8 +30,14 @@ public class AiChatModelSaveReqVO { | ||
| 27 | 30 | ||
| 28 | @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI") | 31 | @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI") |
| 29 | @NotEmpty(message = "模型平台不能为空") | 32 | @NotEmpty(message = "模型平台不能为空") |
| 33 | + @InEnum(AiPlatformEnum.class) | ||
| 30 | private String platform; | 34 | private String platform; |
| 31 | 35 | ||
| 36 | + @Schema(description = "模型类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 37 | + @NotNull(message = "模型类型不能为空") | ||
| 38 | + @InEnum(AiModelTypeEnum.class) | ||
| 39 | + private Integer type; | ||
| 40 | + | ||
| 32 | @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | 41 | @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| 33 | @NotNull(message = "排序不能为空") | 42 | @NotNull(message = "排序不能为空") |
| 34 | private Integer sort; | 43 | private Integer sort; |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 5 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | ||
| 6 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 7 | +import lombok.Data; | ||
| 8 | +import lombok.EqualsAndHashCode; | ||
| 9 | +import lombok.ToString; | ||
| 10 | +import org.springframework.format.annotation.DateTimeFormat; | ||
| 11 | + | ||
| 12 | +import java.time.LocalDateTime; | ||
| 13 | + | ||
| 14 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | ||
| 15 | + | ||
| 16 | +@Schema(description = "管理后台 - AI 工具分页 Request VO") | ||
| 17 | +@Data | ||
| 18 | +public class AiToolPageReqVO extends PageParam { | ||
| 19 | + | ||
| 20 | + @Schema(description = "工具名称", example = "王五") | ||
| 21 | + private String name; | ||
| 22 | + | ||
| 23 | + @Schema(description = "工具描述", example = "你猜") | ||
| 24 | + private String description; | ||
| 25 | + | ||
| 26 | + @Schema(description = "状态", example = "1") | ||
| 27 | + @InEnum(CommonStatusEnum.class) | ||
| 28 | + private Integer status; | ||
| 29 | + | ||
| 30 | + @Schema(description = "创建时间") | ||
| 31 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | ||
| 32 | + private LocalDateTime[] createTime; | ||
| 33 | + | ||
| 34 | +} | ||
| 0 | \ No newline at end of file | 35 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.*; | ||
| 5 | + | ||
| 6 | +import java.time.LocalDateTime; | ||
| 7 | + | ||
| 8 | +@Schema(description = "管理后台 - AI 工具 Response VO") | ||
| 9 | +@Data | ||
| 10 | +public class AiToolRespVO { | ||
| 11 | + | ||
| 12 | + @Schema(description = "工具编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19661") | ||
| 13 | + private Long id; | ||
| 14 | + | ||
| 15 | + @Schema(description = "工具名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") | ||
| 16 | + private String name; | ||
| 17 | + | ||
| 18 | + @Schema(description = "工具描述", example = "你猜") | ||
| 19 | + private String description; | ||
| 20 | + | ||
| 21 | + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 22 | + private Integer status; | ||
| 23 | + | ||
| 24 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | ||
| 25 | + private LocalDateTime createTime; | ||
| 26 | + | ||
| 27 | +} | ||
| 0 | \ No newline at end of file | 28 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | ||
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 6 | +import jakarta.validation.constraints.NotEmpty; | ||
| 7 | +import lombok.Data; | ||
| 8 | + | ||
| 9 | +@Schema(description = "管理后台 - AI 工具新增/修改 Request VO") | ||
| 10 | +@Data | ||
| 11 | +public class AiToolSaveReqVO { | ||
| 12 | + | ||
| 13 | + @Schema(description = "工具编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19661") | ||
| 14 | + private Long id; | ||
| 15 | + | ||
| 16 | + @Schema(description = "工具名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") | ||
| 17 | + @NotEmpty(message = "工具名称不能为空") | ||
| 18 | + private String name; | ||
| 19 | + | ||
| 20 | + @Schema(description = "工具描述", example = "你猜") | ||
| 21 | + private String description; | ||
| 22 | + | ||
| 23 | + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 24 | + @InEnum(CommonStatusEnum.class) | ||
| 25 | + private Integer status; | ||
| 26 | + | ||
| 27 | +} | ||
| 0 | \ No newline at end of file | 28 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java
| @@ -12,7 +12,6 @@ import io.swagger.v3.oas.annotations.Operation; | @@ -12,7 +12,6 @@ import io.swagger.v3.oas.annotations.Operation; | ||
| 12 | import io.swagger.v3.oas.annotations.Parameter; | 12 | import io.swagger.v3.oas.annotations.Parameter; |
| 13 | import io.swagger.v3.oas.annotations.tags.Tag; | 13 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 14 | import jakarta.annotation.Resource; | 14 | import jakarta.annotation.Resource; |
| 15 | -import jakarta.annotation.security.PermitAll; | ||
| 16 | import jakarta.validation.Valid; | 15 | import jakarta.validation.Valid; |
| 17 | import org.springframework.http.MediaType; | 16 | import org.springframework.http.MediaType; |
| 18 | import org.springframework.security.access.prepost.PreAuthorize; | 17 | import org.springframework.security.access.prepost.PreAuthorize; |
| @@ -32,7 +31,6 @@ public class AiWriteController { | @@ -32,7 +31,6 @@ public class AiWriteController { | ||
| 32 | 31 | ||
| 33 | @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) | 32 | @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) |
| 34 | @Operation(summary = "写作生成(流式)", description = "流式返回,响应较快") | 33 | @Operation(summary = "写作生成(流式)", description = "流式返回,响应较快") |
| 35 | - @PermitAll // 解决 SSE 最终响应的时候,会被 Access Denied 拦截的问题 | ||
| 36 | public Flux<CommonResult<String>> generateWriteContent(@RequestBody @Valid AiWriteGenerateReqVO generateReqVO) { | 34 | public Flux<CommonResult<String>> generateWriteContent(@RequestBody @Valid AiWriteGenerateReqVO generateReqVO) { |
| 37 | return writeService.generateWriteContent(generateReqVO, getLoginUserId()); | 35 | return writeService.generateWriteContent(generateReqVO, getLoginUserId()); |
| 38 | } | 36 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWritePageReqVO.java
| @@ -13,8 +13,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ | @@ -13,8 +13,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ | ||
| 13 | 13 | ||
| 14 | @Schema(description = "管理后台 - AI 写作分页 Request VO") | 14 | @Schema(description = "管理后台 - AI 写作分页 Request VO") |
| 15 | @Data | 15 | @Data |
| 16 | -@EqualsAndHashCode(callSuper = true) | ||
| 17 | -@ToString(callSuper = true) | ||
| 18 | public class AiWritePageReqVO extends PageParam { | 16 | public class AiWritePageReqVO extends PageParam { |
| 19 | 17 | ||
| 20 | @Schema(description = "用户编号", example = "28404") | 18 | @Schema(description = "用户编号", example = "28404") |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java
| 1 | package cn.iocoder.yudao.module.ai.dal.dataobject.chat; | 1 | package cn.iocoder.yudao.module.ai.dal.dataobject.chat; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 4 | -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; | ||
| 5 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | ||
| 6 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | 4 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; |
| 5 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; | ||
| 7 | import com.baomidou.mybatisplus.annotation.KeySequence; | 6 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 8 | import com.baomidou.mybatisplus.annotation.TableId; | 7 | import com.baomidou.mybatisplus.annotation.TableId; |
| 9 | import com.baomidou.mybatisplus.annotation.TableName; | 8 | import com.baomidou.mybatisplus.annotation.TableName; |
| @@ -22,7 +21,6 @@ import java.time.LocalDateTime; | @@ -22,7 +21,6 @@ import java.time.LocalDateTime; | ||
| 22 | @TableName("ai_chat_conversation") | 21 | @TableName("ai_chat_conversation") |
| 23 | @KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 | 22 | @KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 |
| 24 | @Data | 23 | @Data |
| 25 | -@EqualsAndHashCode(callSuper = true) | ||
| 26 | @Builder | 24 | @Builder |
| 27 | @NoArgsConstructor | 25 | @NoArgsConstructor |
| 28 | @AllArgsConstructor | 26 | @AllArgsConstructor |
| @@ -66,20 +64,15 @@ public class AiChatConversationDO extends BaseDO { | @@ -66,20 +64,15 @@ public class AiChatConversationDO extends BaseDO { | ||
| 66 | private Long roleId; | 64 | private Long roleId; |
| 67 | 65 | ||
| 68 | /** | 66 | /** |
| 69 | - * 知识库编号 | ||
| 70 | - * <p> | ||
| 71 | - * 关联 {@link AiKnowledgeDO#getId()} | ||
| 72 | - */ | ||
| 73 | - private Long knowledgeId; | ||
| 74 | - | ||
| 75 | - /** | ||
| 76 | * 模型编号 | 67 | * 模型编号 |
| 77 | * | 68 | * |
| 78 | - * 关联 {@link AiChatModelDO#getId()} 字段 | 69 | + * 关联 {@link AiModelDO#getId()} 字段 |
| 79 | */ | 70 | */ |
| 80 | private Long modelId; | 71 | private Long modelId; |
| 81 | /** | 72 | /** |
| 82 | * 模型标志 | 73 | * 模型标志 |
| 74 | + * | ||
| 75 | + * 冗余 {@link AiModelDO#getModel()} 字段 | ||
| 83 | */ | 76 | */ |
| 84 | private String model; | 77 | private String model; |
| 85 | 78 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java
| 1 | package cn.iocoder.yudao.module.ai.dal.dataobject.chat; | 1 | package cn.iocoder.yudao.module.ai.dal.dataobject.chat; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; | ||
| 4 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; | 5 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; |
| 5 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | ||
| 6 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | 6 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; |
| 7 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; | ||
| 7 | import com.baomidou.mybatisplus.annotation.KeySequence; | 8 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 8 | import com.baomidou.mybatisplus.annotation.TableField; | 9 | import com.baomidou.mybatisplus.annotation.TableField; |
| 9 | import com.baomidou.mybatisplus.annotation.TableId; | 10 | import com.baomidou.mybatisplus.annotation.TableId; |
| 10 | import com.baomidou.mybatisplus.annotation.TableName; | 11 | import com.baomidou.mybatisplus.annotation.TableName; |
| 11 | -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; | ||
| 12 | import lombok.*; | 12 | import lombok.*; |
| 13 | import org.springframework.ai.chat.messages.MessageType; | 13 | import org.springframework.ai.chat.messages.MessageType; |
| 14 | 14 | ||
| @@ -20,10 +20,9 @@ import java.util.List; | @@ -20,10 +20,9 @@ import java.util.List; | ||
| 20 | * @since 2024/4/14 17:35 | 20 | * @since 2024/4/14 17:35 |
| 21 | * @since 2024/4/14 17:35 | 21 | * @since 2024/4/14 17:35 |
| 22 | */ | 22 | */ |
| 23 | -@TableName("ai_chat_message") | 23 | +@TableName(value = "ai_chat_message", autoResultMap = true) |
| 24 | @KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 | 24 | @KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 |
| 25 | @Data | 25 | @Data |
| 26 | -@EqualsAndHashCode(callSuper = true) | ||
| 27 | @Builder | 26 | @Builder |
| 28 | @NoArgsConstructor | 27 | @NoArgsConstructor |
| 29 | @AllArgsConstructor | 28 | @AllArgsConstructor |
| @@ -71,23 +70,16 @@ public class AiChatMessageDO extends BaseDO { | @@ -71,23 +70,16 @@ public class AiChatMessageDO extends BaseDO { | ||
| 71 | */ | 70 | */ |
| 72 | private Long roleId; | 71 | private Long roleId; |
| 73 | 72 | ||
| 74 | - | ||
| 75 | - /** | ||
| 76 | - * 段落编号数组 | ||
| 77 | - * | ||
| 78 | - * 关联 {@link AiKnowledgeSegmentDO#getId()} 字段 | ||
| 79 | - */ | ||
| 80 | - @TableField(typeHandler = JacksonTypeHandler.class) | ||
| 81 | - private List<Long> segmentIds; | ||
| 82 | - | ||
| 83 | /** | 73 | /** |
| 84 | * 模型标志 | 74 | * 模型标志 |
| 75 | + * | ||
| 76 | + * 冗余 {@link AiModelDO#getModel()} | ||
| 85 | */ | 77 | */ |
| 86 | private String model; | 78 | private String model; |
| 87 | /** | 79 | /** |
| 88 | * 模型编号 | 80 | * 模型编号 |
| 89 | * | 81 | * |
| 90 | - * 关联 {@link AiChatModelDO#getId()} 字段 | 82 | + * 关联 {@link AiModelDO#getId()} 字段 |
| 91 | */ | 83 | */ |
| 92 | private Long modelId; | 84 | private Long modelId; |
| 93 | 85 | ||
| @@ -101,4 +93,12 @@ public class AiChatMessageDO extends BaseDO { | @@ -101,4 +93,12 @@ public class AiChatMessageDO extends BaseDO { | ||
| 101 | */ | 93 | */ |
| 102 | private Boolean useContext; | 94 | private Boolean useContext; |
| 103 | 95 | ||
| 96 | + /** | ||
| 97 | + * 知识库段落编号数组 | ||
| 98 | + * | ||
| 99 | + * 关联 {@link AiKnowledgeSegmentDO#getId()} 字段 | ||
| 100 | + */ | ||
| 101 | + @TableField(typeHandler = LongListTypeHandler.class) | ||
| 102 | + private List<Long> segmentIds; | ||
| 103 | + | ||
| 104 | } | 104 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java
| @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.image; | @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.image; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | 3 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 5 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | 5 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; |
| 6 | import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; | 6 | import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; |
| 7 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | 7 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| 8 | import com.baomidou.mybatisplus.annotation.KeySequence; | 8 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| @@ -53,9 +53,15 @@ public class AiImageDO extends BaseDO { | @@ -53,9 +53,15 @@ public class AiImageDO extends BaseDO { | ||
| 53 | */ | 53 | */ |
| 54 | private String platform; | 54 | private String platform; |
| 55 | /** | 55 | /** |
| 56 | - * 模型 | 56 | + * 模型编号 |
| 57 | * | 57 | * |
| 58 | - * 冗余 {@link AiChatModelDO#getModel()} | 58 | + * 关联 {@link AiModelDO#getId()} |
| 59 | + */ | ||
| 60 | + private Long modelId; | ||
| 61 | + /** | ||
| 62 | + * 模型标识 | ||
| 63 | + * | ||
| 64 | + * 冗余 {@link AiModelDO#getModel()} | ||
| 59 | */ | 65 | */ |
| 60 | private String model; | 66 | private String model; |
| 61 | 67 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
| @@ -2,15 +2,12 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; | @@ -2,15 +2,12 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 5 | -import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; | 5 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; |
| 6 | import com.baomidou.mybatisplus.annotation.KeySequence; | 6 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 7 | -import com.baomidou.mybatisplus.annotation.TableField; | ||
| 8 | import com.baomidou.mybatisplus.annotation.TableId; | 7 | import com.baomidou.mybatisplus.annotation.TableId; |
| 9 | import com.baomidou.mybatisplus.annotation.TableName; | 8 | import com.baomidou.mybatisplus.annotation.TableName; |
| 10 | import lombok.Data; | 9 | import lombok.Data; |
| 11 | 10 | ||
| 12 | -import java.util.List; | ||
| 13 | - | ||
| 14 | /** | 11 | /** |
| 15 | * AI 知识库 DO | 12 | * AI 知识库 DO |
| 16 | * | 13 | * |
| @@ -27,12 +24,6 @@ public class AiKnowledgeDO extends BaseDO { | @@ -27,12 +24,6 @@ public class AiKnowledgeDO extends BaseDO { | ||
| 27 | @TableId | 24 | @TableId |
| 28 | private Long id; | 25 | private Long id; |
| 29 | /** | 26 | /** |
| 30 | - * 用户编号 | ||
| 31 | - * <p> | ||
| 32 | - * 关联 AdminUserDO 的 userId 字段 | ||
| 33 | - */ | ||
| 34 | - private Long userId; | ||
| 35 | - /** | ||
| 36 | * 知识库名称 | 27 | * 知识库名称 |
| 37 | */ | 28 | */ |
| 38 | private String name; | 29 | private String name; |
| @@ -42,20 +33,17 @@ public class AiKnowledgeDO extends BaseDO { | @@ -42,20 +33,17 @@ public class AiKnowledgeDO extends BaseDO { | ||
| 42 | private String description; | 33 | private String description; |
| 43 | 34 | ||
| 44 | /** | 35 | /** |
| 45 | - * 可见权限,选择哪些人可见 | ||
| 46 | - * <p> | ||
| 47 | - * -1 所有人可见,其他为各自用户编号 | ||
| 48 | - */ | ||
| 49 | - @TableField(typeHandler = LongListTypeHandler.class) | ||
| 50 | - private List<Long> visibilityPermissions; | ||
| 51 | - /** | ||
| 52 | - * 嵌入模型编号 | 36 | + * 向量模型编号 |
| 37 | + * | ||
| 38 | + * 关联 {@link AiModelDO#getId()} | ||
| 53 | */ | 39 | */ |
| 54 | - private Long modelId; | 40 | + private Long embeddingModelId; |
| 55 | /** | 41 | /** |
| 56 | * 模型标识 | 42 | * 模型标识 |
| 43 | + * | ||
| 44 | + * 冗余 {@link AiModelDO#getModel()} | ||
| 57 | */ | 45 | */ |
| 58 | - private String model; | 46 | + private String embeddingModel; |
| 59 | 47 | ||
| 60 | /** | 48 | /** |
| 61 | * topK | 49 | * topK |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
| @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; | @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 5 | -import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum; | ||
| 6 | import com.baomidou.mybatisplus.annotation.KeySequence; | 5 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 7 | import com.baomidou.mybatisplus.annotation.TableId; | 6 | import com.baomidou.mybatisplus.annotation.TableId; |
| 8 | import com.baomidou.mybatisplus.annotation.TableName; | 7 | import com.baomidou.mybatisplus.annotation.TableName; |
| @@ -30,57 +29,35 @@ public class AiKnowledgeDocumentDO extends BaseDO { | @@ -30,57 +29,35 @@ public class AiKnowledgeDocumentDO extends BaseDO { | ||
| 30 | */ | 29 | */ |
| 31 | private Long knowledgeId; | 30 | private Long knowledgeId; |
| 32 | /** | 31 | /** |
| 33 | - * 文件名称 | 32 | + * 文档名称 |
| 34 | */ | 33 | */ |
| 35 | private String name; | 34 | private String name; |
| 36 | /** | 35 | /** |
| 37 | - * 内容 | ||
| 38 | - */ | ||
| 39 | - private String content; | ||
| 40 | - /** | ||
| 41 | * 文件 URL | 36 | * 文件 URL |
| 42 | */ | 37 | */ |
| 43 | private String url; | 38 | private String url; |
| 44 | /** | 39 | /** |
| 45 | - * 文档 token 数量 | 40 | + * 内容 |
| 46 | */ | 41 | */ |
| 47 | - private Integer tokens; | 42 | + private String content; |
| 48 | /** | 43 | /** |
| 49 | - * 文档字符数 | 44 | + * 文档长度 |
| 50 | */ | 45 | */ |
| 51 | - private Integer wordCount; | ||
| 52 | - | 46 | + private Integer contentLength; |
| 53 | 47 | ||
| 54 | - // ========== 自定义分段所用参数 ========== | ||
| 55 | - // TODO @新:3)defaultChunkSize、defaultChunkSize、minChunkSizeChars、maxNumChunks 这几个字段的命名,可能要微信一起讨论下。尽量命名保持风格统一哈。 | ||
| 56 | /** | 48 | /** |
| 57 | - * 每个文本块的目标 token 数 | ||
| 58 | - */ | ||
| 59 | - private Integer defaultSegmentTokens; | ||
| 60 | - /** | ||
| 61 | - * 每个文本块的最小字符数 | ||
| 62 | - */ | ||
| 63 | - private Integer minSegmentWordCount; | ||
| 64 | - /** | ||
| 65 | - * 低于此值的块会被丢弃 | ||
| 66 | - */ | ||
| 67 | - private Integer minChunkLengthToEmbed; | ||
| 68 | - /** | ||
| 69 | - * 最大块数 | 49 | + * 文档 token 数量 |
| 70 | */ | 50 | */ |
| 71 | - private Integer maxNumSegments; | 51 | + private Integer tokens; |
| 72 | /** | 52 | /** |
| 73 | - * 分块是否保留分隔符 | 53 | + * 分片最大 Token 数 |
| 74 | */ | 54 | */ |
| 75 | - private Boolean keepSeparator; | ||
| 76 | - // =================================== | 55 | + private Integer segmentMaxTokens; |
| 77 | 56 | ||
| 78 | /** | 57 | /** |
| 79 | - * 切片状态 | ||
| 80 | - * <p> | ||
| 81 | - * 枚举 {@link AiKnowledgeDocumentStatusEnum} | 58 | + * 召回次数 |
| 82 | */ | 59 | */ |
| 83 | - private Integer sliceStatus; | 60 | + private Integer retrievalCount; |
| 84 | 61 | ||
| 85 | /** | 62 | /** |
| 86 | * 状态 | 63 | * 状态 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
| @@ -17,7 +17,10 @@ import lombok.Data; | @@ -17,7 +17,10 @@ import lombok.Data; | ||
| 17 | @Data | 17 | @Data |
| 18 | public class AiKnowledgeSegmentDO extends BaseDO { | 18 | public class AiKnowledgeSegmentDO extends BaseDO { |
| 19 | 19 | ||
| 20 | - public static final String FIELD_KNOWLEDGE_ID = "knowledgeId"; | 20 | + /** |
| 21 | + * 向量库的编号 - 空值 | ||
| 22 | + */ | ||
| 23 | + public static final String VECTOR_ID_EMPTY = ""; | ||
| 21 | 24 | ||
| 22 | /** | 25 | /** |
| 23 | * 编号 | 26 | * 编号 |
| @@ -25,10 +28,6 @@ public class AiKnowledgeSegmentDO extends BaseDO { | @@ -25,10 +28,6 @@ public class AiKnowledgeSegmentDO extends BaseDO { | ||
| 25 | @TableId | 28 | @TableId |
| 26 | private Long id; | 29 | private Long id; |
| 27 | /** | 30 | /** |
| 28 | - * 向量库的编号 | ||
| 29 | - */ | ||
| 30 | - private String vectorId; | ||
| 31 | - /** | ||
| 32 | * 知识库编号 | 31 | * 知识库编号 |
| 33 | * <p> | 32 | * <p> |
| 34 | * 关联 {@link AiKnowledgeDO#getId()} | 33 | * 关联 {@link AiKnowledgeDO#getId()} |
| @@ -45,13 +44,24 @@ public class AiKnowledgeSegmentDO extends BaseDO { | @@ -45,13 +44,24 @@ public class AiKnowledgeSegmentDO extends BaseDO { | ||
| 45 | */ | 44 | */ |
| 46 | private String content; | 45 | private String content; |
| 47 | /** | 46 | /** |
| 48 | - * 字符数 | 47 | + * 切片内容长度 |
| 49 | */ | 48 | */ |
| 50 | - private Integer wordCount; | 49 | + private Integer contentLength; |
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * 向量库的编号 | ||
| 53 | + */ | ||
| 54 | + private String vectorId; | ||
| 51 | /** | 55 | /** |
| 52 | * token 数量 | 56 | * token 数量 |
| 53 | */ | 57 | */ |
| 54 | private Integer tokens; | 58 | private Integer tokens; |
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * 召回次数 | ||
| 62 | + */ | ||
| 63 | + private Integer retrievalCount; | ||
| 64 | + | ||
| 55 | /** | 65 | /** |
| 56 | * 状态 | 66 | * 状态 |
| 57 | * <p> | 67 | * <p> |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java
| @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.mindmap; | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.mindmap; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | 3 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 5 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; | ||
| 5 | import com.baomidou.mybatisplus.annotation.KeySequence; | 6 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 6 | import com.baomidou.mybatisplus.annotation.TableId; | 7 | import com.baomidou.mybatisplus.annotation.TableId; |
| 7 | import com.baomidou.mybatisplus.annotation.TableName; | 8 | import com.baomidou.mybatisplus.annotation.TableName; |
| @@ -37,6 +38,12 @@ public class AiMindMapDO extends BaseDO { | @@ -37,6 +38,12 @@ public class AiMindMapDO extends BaseDO { | ||
| 37 | */ | 38 | */ |
| 38 | private String platform; | 39 | private String platform; |
| 39 | /** | 40 | /** |
| 41 | + * 模型编号 | ||
| 42 | + * | ||
| 43 | + * 关联 {@link AiModelDO#getId()} | ||
| 44 | + */ | ||
| 45 | + private Long modelId; | ||
| 46 | + /** | ||
| 40 | * 模型 | 47 | * 模型 |
| 41 | */ | 48 | */ |
| 42 | private String model; | 49 | private String model; |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java
| @@ -16,7 +16,6 @@ import lombok.*; | @@ -16,7 +16,6 @@ import lombok.*; | ||
| 16 | @TableName("ai_api_key") | 16 | @TableName("ai_api_key") |
| 17 | @KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 | 17 | @KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 |
| 18 | @Data | 18 | @Data |
| 19 | -@EqualsAndHashCode(callSuper = true) | ||
| 20 | @Builder | 19 | @Builder |
| 21 | @NoArgsConstructor | 20 | @NoArgsConstructor |
| 22 | @AllArgsConstructor | 21 | @AllArgsConstructor |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java
| @@ -2,11 +2,16 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.model; | @@ -2,11 +2,16 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.model; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 5 | +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; | ||
| 6 | +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; | ||
| 5 | import com.baomidou.mybatisplus.annotation.KeySequence; | 7 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 8 | +import com.baomidou.mybatisplus.annotation.TableField; | ||
| 6 | import com.baomidou.mybatisplus.annotation.TableId; | 9 | import com.baomidou.mybatisplus.annotation.TableId; |
| 7 | import com.baomidou.mybatisplus.annotation.TableName; | 10 | import com.baomidou.mybatisplus.annotation.TableName; |
| 8 | import lombok.*; | 11 | import lombok.*; |
| 9 | 12 | ||
| 13 | +import java.util.List; | ||
| 14 | + | ||
| 10 | /** | 15 | /** |
| 11 | * AI 聊天角色 DO | 16 | * AI 聊天角色 DO |
| 12 | * | 17 | * |
| @@ -16,7 +21,6 @@ import lombok.*; | @@ -16,7 +21,6 @@ import lombok.*; | ||
| 16 | @TableName(value = "ai_chat_role", autoResultMap = true) | 21 | @TableName(value = "ai_chat_role", autoResultMap = true) |
| 17 | @KeySequence("ai_chat_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 | 22 | @KeySequence("ai_chat_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 |
| 18 | @Data | 23 | @Data |
| 19 | -@EqualsAndHashCode(callSuper = true) | ||
| 20 | @Builder | 24 | @Builder |
| 21 | @NoArgsConstructor | 25 | @NoArgsConstructor |
| 22 | @AllArgsConstructor | 26 | @AllArgsConstructor |
| @@ -58,11 +62,26 @@ public class AiChatRoleDO extends BaseDO { | @@ -58,11 +62,26 @@ public class AiChatRoleDO extends BaseDO { | ||
| 58 | /** | 62 | /** |
| 59 | * 模型编号 | 63 | * 模型编号 |
| 60 | * | 64 | * |
| 61 | - * 关联 {@link AiChatModelDO#getId()} 字段 | 65 | + * 关联 {@link AiModelDO#getId()} 字段 |
| 62 | */ | 66 | */ |
| 63 | private Long modelId; | 67 | private Long modelId; |
| 64 | 68 | ||
| 65 | /** | 69 | /** |
| 70 | + * 引用的知识库编号列表 | ||
| 71 | + * | ||
| 72 | + * 关联 {@link AiKnowledgeDO#getId()} 字段 | ||
| 73 | + */ | ||
| 74 | + @TableField(typeHandler = LongListTypeHandler.class) | ||
| 75 | + private List<Long> knowledgeIds; | ||
| 76 | + /** | ||
| 77 | + * 引用的工具编号列表 | ||
| 78 | + * | ||
| 79 | + * 关联 {@link AiToolDO#getId()} 字段 | ||
| 80 | + */ | ||
| 81 | + @TableField(typeHandler = LongListTypeHandler.class) | ||
| 82 | + private List<Long> toolIds; | ||
| 83 | + | ||
| 84 | + /** | ||
| 66 | * 是否公开 | 85 | * 是否公开 |
| 67 | * | 86 | * |
| 68 | * 1. true - 公开;由管理员在【角色管理】所创建 | 87 | * 1. true - 公开;由管理员在【角色管理】所创建 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatModelDO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java
| 1 | package cn.iocoder.yudao.module.ai.dal.dataobject.model; | 1 | package cn.iocoder.yudao.module.ai.dal.dataobject.model; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum; | ||
| 3 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | 4 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; |
| 4 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 5 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 5 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 6 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| @@ -9,21 +10,20 @@ import com.baomidou.mybatisplus.annotation.TableName; | @@ -9,21 +10,20 @@ import com.baomidou.mybatisplus.annotation.TableName; | ||
| 9 | import lombok.*; | 10 | import lombok.*; |
| 10 | 11 | ||
| 11 | /** | 12 | /** |
| 12 | - * AI 聊天模型 DO | 13 | + * AI 模型 DO |
| 13 | * | 14 | * |
| 14 | - * 默认聊天模型:{@link #status} 为开启,并且 {@link #sort} 排序第一 | 15 | + * 默认模型:{@link #status} 为开启,并且 {@link #sort} 排序第一 |
| 15 | * | 16 | * |
| 16 | * @author fansili | 17 | * @author fansili |
| 17 | * @since 2024/4/24 19:39 | 18 | * @since 2024/4/24 19:39 |
| 18 | */ | 19 | */ |
| 19 | -@TableName("ai_chat_model") | ||
| 20 | -@KeySequence("ai_chat_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 | 20 | +@TableName("ai_model") |
| 21 | +@KeySequence("ai_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 | ||
| 21 | @Data | 22 | @Data |
| 22 | -@EqualsAndHashCode(callSuper = true) | ||
| 23 | @Builder | 23 | @Builder |
| 24 | @NoArgsConstructor | 24 | @NoArgsConstructor |
| 25 | @AllArgsConstructor | 25 | @AllArgsConstructor |
| 26 | -public class AiChatModelDO extends BaseDO { | 26 | +public class AiModelDO extends BaseDO { |
| 27 | 27 | ||
| 28 | /** | 28 | /** |
| 29 | * 编号 | 29 | * 编号 |
| @@ -50,6 +50,12 @@ public class AiChatModelDO extends BaseDO { | @@ -50,6 +50,12 @@ public class AiChatModelDO extends BaseDO { | ||
| 50 | * 枚举 {@link AiPlatformEnum} | 50 | * 枚举 {@link AiPlatformEnum} |
| 51 | */ | 51 | */ |
| 52 | private String platform; | 52 | private String platform; |
| 53 | + /** | ||
| 54 | + * 类型 | ||
| 55 | + * | ||
| 56 | + * 枚举 {@link AiModelTypeEnum} | ||
| 57 | + */ | ||
| 58 | + private Integer type; | ||
| 53 | 59 | ||
| 54 | /** | 60 | /** |
| 55 | * 排序值 | 61 | * 排序值 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiToolDO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.dal.dataobject.model; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | ||
| 4 | +import cn.iocoder.yudao.module.ai.service.model.tool.DirectoryListToolFunction; | ||
| 5 | +import cn.iocoder.yudao.module.ai.service.model.tool.WeatherQueryToolFunction; | ||
| 6 | +import com.baomidou.mybatisplus.annotation.KeySequence; | ||
| 7 | +import com.baomidou.mybatisplus.annotation.TableId; | ||
| 8 | +import com.baomidou.mybatisplus.annotation.TableName; | ||
| 9 | +import lombok.*; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * AI 工具 DO | ||
| 13 | + * | ||
| 14 | + * @author 芋道源码 | ||
| 15 | + */ | ||
| 16 | +@TableName("ai_tool") | ||
| 17 | +@KeySequence("ai_tool_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 | ||
| 18 | +@Data | ||
| 19 | +@Builder | ||
| 20 | +@NoArgsConstructor | ||
| 21 | +@AllArgsConstructor | ||
| 22 | +public class AiToolDO extends BaseDO { | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 工具编号 | ||
| 26 | + */ | ||
| 27 | + @TableId | ||
| 28 | + private Long id; | ||
| 29 | + /** | ||
| 30 | + * 工具名称 | ||
| 31 | + * | ||
| 32 | + * 对应 Bean 的名字,例如说: | ||
| 33 | + * 1. {@link DirectoryListToolFunction} 的 Bean 名字是 directory_list | ||
| 34 | + * 2. {@link WeatherQueryToolFunction} 的 Bean 名字是 weather_query | ||
| 35 | + */ | ||
| 36 | + private String name; | ||
| 37 | + /** | ||
| 38 | + * 工具描述 | ||
| 39 | + */ | ||
| 40 | + private String description; | ||
| 41 | + /** | ||
| 42 | + * 状态 | ||
| 43 | + * | ||
| 44 | + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} | ||
| 45 | + */ | ||
| 46 | + private Integer status; | ||
| 47 | + | ||
| 48 | +} | ||
| 0 | \ No newline at end of file | 49 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java
| @@ -84,6 +84,7 @@ public class AiMusicDO extends BaseDO { | @@ -84,6 +84,7 @@ public class AiMusicDO extends BaseDO { | ||
| 84 | * 枚举 {@link AiPlatformEnum} | 84 | * 枚举 {@link AiPlatformEnum} |
| 85 | */ | 85 | */ |
| 86 | private String platform; | 86 | private String platform; |
| 87 | + // TODO @芋艿:modelId? | ||
| 87 | /** | 88 | /** |
| 88 | * 模型 | 89 | * 模型 |
| 89 | */ | 90 | */ |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java
| @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.write; | @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.write; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | 3 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 5 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; | ||
| 6 | +import cn.iocoder.yudao.module.ai.enums.DictTypeConstants; | ||
| 5 | import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum; | 7 | import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum; |
| 6 | import com.baomidou.mybatisplus.annotation.KeySequence; | 8 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 7 | import com.baomidou.mybatisplus.annotation.TableId; | 9 | import com.baomidou.mybatisplus.annotation.TableId; |
| @@ -45,6 +47,12 @@ public class AiWriteDO extends BaseDO { | @@ -45,6 +47,12 @@ public class AiWriteDO extends BaseDO { | ||
| 45 | */ | 47 | */ |
| 46 | private String platform; | 48 | private String platform; |
| 47 | /** | 49 | /** |
| 50 | + * 模型编号 | ||
| 51 | + * | ||
| 52 | + * 关联 {@link AiModelDO#getId()} | ||
| 53 | + */ | ||
| 54 | + private Long modelId; | ||
| 55 | + /** | ||
| 48 | * 模型 | 56 | * 模型 |
| 49 | */ | 57 | */ |
| 50 | private String model; | 58 | private String model; |
| @@ -66,25 +74,25 @@ public class AiWriteDO extends BaseDO { | @@ -66,25 +74,25 @@ public class AiWriteDO extends BaseDO { | ||
| 66 | /** | 74 | /** |
| 67 | * 长度提示词 | 75 | * 长度提示词 |
| 68 | * | 76 | * |
| 69 | - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_LENGTH} | 77 | + * 字典:{@link DictTypeConstants#AI_WRITE_LENGTH} |
| 70 | */ | 78 | */ |
| 71 | private Integer length; | 79 | private Integer length; |
| 72 | /** | 80 | /** |
| 73 | * 格式提示词 | 81 | * 格式提示词 |
| 74 | * | 82 | * |
| 75 | - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_FORMAT} | 83 | + * 字典:{@link DictTypeConstants#AI_WRITE_FORMAT} |
| 76 | */ | 84 | */ |
| 77 | private Integer format; | 85 | private Integer format; |
| 78 | /** | 86 | /** |
| 79 | * 语气提示词 | 87 | * 语气提示词 |
| 80 | * | 88 | * |
| 81 | - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_TONE} | 89 | + * 字典:{@link DictTypeConstants#AI_WRITE_TONE} |
| 82 | */ | 90 | */ |
| 83 | private Integer tone; | 91 | private Integer tone; |
| 84 | /** | 92 | /** |
| 85 | * 语言提示词 | 93 | * 语言提示词 |
| 86 | * | 94 | * |
| 87 | - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_LANGUAGE} | 95 | + * 字典:{@link DictTypeConstants#AI_WRITE_LANGUAGE} |
| 88 | */ | 96 | */ |
| 89 | private Integer language; | 97 | private Integer language; |
| 90 | 98 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java
| @@ -5,10 +5,14 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | @@ -5,10 +5,14 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; | 6 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; |
| 7 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; | 7 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; |
| 8 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | ||
| 8 | import org.apache.ibatis.annotations.Mapper; | 9 | import org.apache.ibatis.annotations.Mapper; |
| 9 | 10 | ||
| 11 | +import java.util.Collection; | ||
| 12 | +import java.util.List; | ||
| 13 | + | ||
| 10 | /** | 14 | /** |
| 11 | - * AI 知识库-文档 Mapper | 15 | + * AI 知识库文档 Mapper |
| 12 | * | 16 | * |
| 13 | * @author xiaoxin | 17 | * @author xiaoxin |
| 14 | */ | 18 | */ |
| @@ -17,8 +21,19 @@ public interface AiKnowledgeDocumentMapper extends BaseMapperX<AiKnowledgeDocume | @@ -17,8 +21,19 @@ public interface AiKnowledgeDocumentMapper extends BaseMapperX<AiKnowledgeDocume | ||
| 17 | 21 | ||
| 18 | default PageResult<AiKnowledgeDocumentDO> selectPage(AiKnowledgeDocumentPageReqVO reqVO) { | 22 | default PageResult<AiKnowledgeDocumentDO> selectPage(AiKnowledgeDocumentPageReqVO reqVO) { |
| 19 | return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeDocumentDO>() | 23 | return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeDocumentDO>() |
| 24 | + .eqIfPresent(AiKnowledgeDocumentDO::getKnowledgeId, reqVO.getKnowledgeId()) | ||
| 20 | .likeIfPresent(AiKnowledgeDocumentDO::getName, reqVO.getName()) | 25 | .likeIfPresent(AiKnowledgeDocumentDO::getName, reqVO.getName()) |
| 21 | .orderByDesc(AiKnowledgeDocumentDO::getId)); | 26 | .orderByDesc(AiKnowledgeDocumentDO::getId)); |
| 22 | } | 27 | } |
| 23 | 28 | ||
| 29 | + default void updateRetrievalCountIncr(Collection<Long> ids) { | ||
| 30 | + update(new LambdaUpdateWrapper<AiKnowledgeDocumentDO>() | ||
| 31 | + .setSql(" retrieval_count = retrieval_count + 1") | ||
| 32 | + .in(AiKnowledgeDocumentDO::getId, ids)); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + default List<AiKnowledgeDocumentDO> selectListByStatus(Integer status) { | ||
| 36 | + return selectList(AiKnowledgeDocumentDO::getStatus, status); | ||
| 37 | + } | ||
| 38 | + | ||
| 24 | } | 39 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java
| 1 | package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; | 1 | package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 6 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | 5 | 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 | @@ -8,19 +7,26 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnow | ||
| 8 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; | 7 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; |
| 9 | import org.apache.ibatis.annotations.Mapper; | 8 | import org.apache.ibatis.annotations.Mapper; |
| 10 | 9 | ||
| 10 | +import java.util.List; | ||
| 11 | + | ||
| 11 | /** | 12 | /** |
| 12 | - * AI 知识库基础信息 Mapper | 13 | + * AI 知识库 Mapper |
| 13 | * | 14 | * |
| 14 | * @author xiaoxin | 15 | * @author xiaoxin |
| 15 | */ | 16 | */ |
| 16 | @Mapper | 17 | @Mapper |
| 17 | public interface AiKnowledgeMapper extends BaseMapperX<AiKnowledgeDO> { | 18 | public interface AiKnowledgeMapper extends BaseMapperX<AiKnowledgeDO> { |
| 18 | 19 | ||
| 19 | - default PageResult<AiKnowledgeDO> selectPage(Long userId, AiKnowledgePageReqVO pageReqVO) { | 20 | + default PageResult<AiKnowledgeDO> selectPage(AiKnowledgePageReqVO pageReqVO) { |
| 20 | return selectPage(pageReqVO, new LambdaQueryWrapperX<AiKnowledgeDO>() | 21 | return selectPage(pageReqVO, new LambdaQueryWrapperX<AiKnowledgeDO>() |
| 21 | - .eq(AiKnowledgeDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) | ||
| 22 | .likeIfPresent(AiKnowledgeDO::getName, pageReqVO.getName()) | 22 | .likeIfPresent(AiKnowledgeDO::getName, pageReqVO.getName()) |
| 23 | - .and(e -> e.apply("FIND_IN_SET(" + userId + ",visibility_permissions)").or(m -> m.apply("FIND_IN_SET(-1,visibility_permissions)"))) | 23 | + .eqIfPresent(AiKnowledgeDO::getStatus, pageReqVO.getStatus()) |
| 24 | + .betweenIfPresent(AiKnowledgeDO::getCreateTime, pageReqVO.getCreateTime()) | ||
| 24 | .orderByDesc(AiKnowledgeDO::getId)); | 25 | .orderByDesc(AiKnowledgeDO::getId)); |
| 25 | } | 26 | } |
| 27 | + | ||
| 28 | + default List<AiKnowledgeDO> selectListByStatus(Integer status) { | ||
| 29 | + return selectList(AiKnowledgeDO::getStatus, status); | ||
| 30 | + } | ||
| 31 | + | ||
| 26 | } | 32 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
| @@ -3,14 +3,19 @@ package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; | @@ -3,14 +3,19 @@ package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; | ||
| 6 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; | 7 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; |
| 8 | +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO; | ||
| 7 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; | 9 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; |
| 10 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | ||
| 11 | +import com.github.yulichang.wrapper.MPJLambdaWrapper; | ||
| 8 | import org.apache.ibatis.annotations.Mapper; | 12 | import org.apache.ibatis.annotations.Mapper; |
| 9 | 13 | ||
| 14 | +import java.util.Collection; | ||
| 10 | import java.util.List; | 15 | import java.util.List; |
| 11 | 16 | ||
| 12 | /** | 17 | /** |
| 13 | - * AI 知识库-分片 Mapper | 18 | + * AI 知识库分片 Mapper |
| 14 | * | 19 | * |
| 15 | * @author xiaoxin | 20 | * @author xiaoxin |
| 16 | */ | 21 | */ |
| @@ -20,8 +25,8 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX<AiKnowledgeSegment | @@ -20,8 +25,8 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX<AiKnowledgeSegment | ||
| 20 | default PageResult<AiKnowledgeSegmentDO> selectPage(AiKnowledgeSegmentPageReqVO reqVO) { | 25 | default PageResult<AiKnowledgeSegmentDO> selectPage(AiKnowledgeSegmentPageReqVO reqVO) { |
| 21 | return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeSegmentDO>() | 26 | return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeSegmentDO>() |
| 22 | .eq(AiKnowledgeSegmentDO::getDocumentId, reqVO.getDocumentId()) | 27 | .eq(AiKnowledgeSegmentDO::getDocumentId, reqVO.getDocumentId()) |
| 28 | + .likeIfPresent(AiKnowledgeSegmentDO::getContent, reqVO.getContent()) | ||
| 23 | .eqIfPresent(AiKnowledgeSegmentDO::getStatus, reqVO.getStatus()) | 29 | .eqIfPresent(AiKnowledgeSegmentDO::getStatus, reqVO.getStatus()) |
| 24 | - .likeIfPresent(AiKnowledgeSegmentDO::getContent, reqVO.getKeyword()) | ||
| 25 | .orderByDesc(AiKnowledgeSegmentDO::getId)); | 30 | .orderByDesc(AiKnowledgeSegmentDO::getId)); |
| 26 | } | 31 | } |
| 27 | 32 | ||
| @@ -31,4 +36,32 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX<AiKnowledgeSegment | @@ -31,4 +36,32 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX<AiKnowledgeSegment | ||
| 31 | .orderByDesc(AiKnowledgeSegmentDO::getId)); | 36 | .orderByDesc(AiKnowledgeSegmentDO::getId)); |
| 32 | } | 37 | } |
| 33 | 38 | ||
| 39 | + default List<AiKnowledgeSegmentDO> selectListByDocumentId(Long documentId) { | ||
| 40 | + return selectList(new LambdaQueryWrapperX<AiKnowledgeSegmentDO>() | ||
| 41 | + .eq(AiKnowledgeSegmentDO::getDocumentId, documentId) | ||
| 42 | + .orderByDesc(AiKnowledgeSegmentDO::getId)); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + default List<AiKnowledgeSegmentDO> selectListByKnowledgeIdAndStatus(Long knowledgeId, Integer status) { | ||
| 46 | + return selectList(AiKnowledgeSegmentDO::getKnowledgeId, knowledgeId, | ||
| 47 | + AiKnowledgeSegmentDO::getStatus, status); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + default List<AiKnowledgeSegmentProcessRespVO> selectProcessList(Collection<Long> documentIds) { | ||
| 51 | + MPJLambdaWrapper<AiKnowledgeSegmentDO> wrapper = new MPJLambdaWrapperX<AiKnowledgeSegmentDO>() | ||
| 52 | + .selectAs(AiKnowledgeSegmentDO::getDocumentId, AiKnowledgeSegmentProcessRespVO::getDocumentId) | ||
| 53 | + .selectCount(AiKnowledgeSegmentDO::getId, "count") | ||
| 54 | + .select("COUNT(CASE WHEN vector_id > '" + AiKnowledgeSegmentDO.VECTOR_ID_EMPTY | ||
| 55 | + + "' THEN 1 ELSE NULL END) AS embeddingCount") | ||
| 56 | + .in(AiKnowledgeSegmentDO::getDocumentId, documentIds) | ||
| 57 | + .groupBy(AiKnowledgeSegmentDO::getDocumentId); | ||
| 58 | + return selectJoinList(AiKnowledgeSegmentProcessRespVO.class, wrapper); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + default void updateRetrievalCountIncrByIds(List<Long> ids) { | ||
| 62 | + update(new LambdaUpdateWrapper<AiKnowledgeSegmentDO>() | ||
| 63 | + .setSql(" retrieval_count = retrieval_count + 1") | ||
| 64 | + .in(AiKnowledgeSegmentDO::getId, ids)); | ||
| 65 | + } | ||
| 66 | + | ||
| 34 | } | 67 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.dal.mysql.model; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 6 | +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; | ||
| 7 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO; | ||
| 8 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; | ||
| 9 | +import org.apache.ibatis.annotations.Mapper; | ||
| 10 | + | ||
| 11 | +import javax.annotation.Nullable; | ||
| 12 | +import java.util.List; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * API 模型 Mapper | ||
| 16 | + * | ||
| 17 | + * @author fansili | ||
| 18 | + */ | ||
| 19 | +@Mapper | ||
| 20 | +public interface AiChatMapper extends BaseMapperX<AiModelDO> { | ||
| 21 | + | ||
| 22 | + default AiModelDO selectFirstByStatus(Integer type, Integer status) { | ||
| 23 | + return selectOne(new QueryWrapperX<AiModelDO>() | ||
| 24 | + .eq("type", type) | ||
| 25 | + .eq("status", status) | ||
| 26 | + .limitN(1) | ||
| 27 | + .orderByAsc("sort")); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + default PageResult<AiModelDO> selectPage(AiModelPageReqVO reqVO) { | ||
| 31 | + return selectPage(reqVO, new LambdaQueryWrapperX<AiModelDO>() | ||
| 32 | + .likeIfPresent(AiModelDO::getName, reqVO.getName()) | ||
| 33 | + .eqIfPresent(AiModelDO::getModel, reqVO.getModel()) | ||
| 34 | + .eqIfPresent(AiModelDO::getPlatform, reqVO.getPlatform()) | ||
| 35 | + .orderByAsc(AiModelDO::getSort)); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + default List<AiModelDO> selectListByStatusAndType(Integer status, Integer type, | ||
| 39 | + @Nullable String platform) { | ||
| 40 | + return selectList(new LambdaQueryWrapperX<AiModelDO>() | ||
| 41 | + .eq(AiModelDO::getStatus, status) | ||
| 42 | + .eq(AiModelDO::getType, type) | ||
| 43 | + .eqIfPresent(AiModelDO::getPlatform, platform) | ||
| 44 | + .orderByAsc(AiModelDO::getSort)); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | +} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatModelMapper.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.dal.mysql.model; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 6 | -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; | ||
| 7 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO; | ||
| 8 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | ||
| 9 | -import org.apache.ibatis.annotations.Mapper; | ||
| 10 | - | ||
| 11 | -import java.util.Collection; | ||
| 12 | -import java.util.List; | ||
| 13 | - | ||
| 14 | -/** | ||
| 15 | - * API 聊天模型 Mapper | ||
| 16 | - * | ||
| 17 | - * @author fansili | ||
| 18 | - */ | ||
| 19 | -@Mapper | ||
| 20 | -public interface AiChatModelMapper extends BaseMapperX<AiChatModelDO> { | ||
| 21 | - | ||
| 22 | - default AiChatModelDO selectFirstByStatus(Integer status) { | ||
| 23 | - return selectOne(new QueryWrapperX<AiChatModelDO>() | ||
| 24 | - .eq("status", status) | ||
| 25 | - .limitN(1) | ||
| 26 | - .orderByAsc("sort")); | ||
| 27 | - } | ||
| 28 | - | ||
| 29 | - default PageResult<AiChatModelDO> selectPage(AiChatModelPageReqVO reqVO) { | ||
| 30 | - return selectPage(reqVO, new LambdaQueryWrapperX<AiChatModelDO>() | ||
| 31 | - .likeIfPresent(AiChatModelDO::getName, reqVO.getName()) | ||
| 32 | - .eqIfPresent(AiChatModelDO::getModel, reqVO.getModel()) | ||
| 33 | - .eqIfPresent(AiChatModelDO::getPlatform, reqVO.getPlatform()) | ||
| 34 | - .orderByAsc(AiChatModelDO::getSort)); | ||
| 35 | - } | ||
| 36 | - | ||
| 37 | - default List<AiChatModelDO> selectList(Integer status) { | ||
| 38 | - return selectList(new LambdaQueryWrapperX<AiChatModelDO>() | ||
| 39 | - .eq(AiChatModelDO::getStatus, status) | ||
| 40 | - .orderByAsc(AiChatModelDO::getSort)); | ||
| 41 | - } | ||
| 42 | - | ||
| 43 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiToolMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.dal.mysql.model; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 6 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; | ||
| 8 | +import org.apache.ibatis.annotations.Mapper; | ||
| 9 | + | ||
| 10 | +import java.util.List; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * AI 工具 Mapper | ||
| 14 | + * | ||
| 15 | + * @author 芋道源码 | ||
| 16 | + */ | ||
| 17 | +@Mapper | ||
| 18 | +public interface AiToolMapper extends BaseMapperX<AiToolDO> { | ||
| 19 | + | ||
| 20 | + default PageResult<AiToolDO> selectPage(AiToolPageReqVO reqVO) { | ||
| 21 | + return selectPage(reqVO, new LambdaQueryWrapperX<AiToolDO>() | ||
| 22 | + .likeIfPresent(AiToolDO::getName, reqVO.getName()) | ||
| 23 | + .eqIfPresent(AiToolDO::getDescription, reqVO.getDescription()) | ||
| 24 | + .eqIfPresent(AiToolDO::getStatus, reqVO.getStatus()) | ||
| 25 | + .betweenIfPresent(AiToolDO::getCreateTime, reqVO.getCreateTime()) | ||
| 26 | + .orderByDesc(AiToolDO::getId)); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + default List<AiToolDO> selectListByStatus(Integer status) { | ||
| 30 | + return selectList(new LambdaQueryWrapperX<AiToolDO>() | ||
| 31 | + .eq(AiToolDO::getStatus, status) | ||
| 32 | + .orderByDesc(AiToolDO::getId)); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | +} | ||
| 0 | \ No newline at end of file | 36 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/framework/web/package-info.java
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java
| @@ -4,17 +4,18 @@ import cn.hutool.core.collection.CollUtil; | @@ -4,17 +4,18 @@ import cn.hutool.core.collection.CollUtil; | ||
| 4 | import cn.hutool.core.lang.Assert; | 4 | import cn.hutool.core.lang.Assert; |
| 5 | import cn.hutool.core.util.ObjUtil; | 5 | import cn.hutool.core.util.ObjUtil; |
| 6 | import cn.hutool.core.util.ObjectUtil; | 6 | import cn.hutool.core.util.ObjectUtil; |
| 7 | +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum; | ||
| 7 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 8 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 8 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 9 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 9 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO; | 10 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO; |
| 10 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; | 11 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; |
| 11 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO; | 12 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO; |
| 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; | 13 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; |
| 13 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | 14 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; |
| 14 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | 15 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; |
| 15 | import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper; | 16 | import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper; |
| 16 | import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService; | 17 | import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService; |
| 17 | -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; | 18 | +import cn.iocoder.yudao.module.ai.service.model.AiModelService; |
| 18 | import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; | 19 | import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; |
| 19 | import jakarta.annotation.Resource; | 20 | import jakarta.annotation.Resource; |
| 20 | import lombok.extern.slf4j.Slf4j; | 21 | import lombok.extern.slf4j.Slf4j; |
| @@ -44,7 +45,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | @@ -44,7 +45,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | ||
| 44 | private AiChatConversationMapper chatConversationMapper; | 45 | private AiChatConversationMapper chatConversationMapper; |
| 45 | 46 | ||
| 46 | @Resource | 47 | @Resource |
| 47 | - private AiChatModelService chatModalService; | 48 | + private AiModelService modalService; |
| 48 | @Resource | 49 | @Resource |
| 49 | private AiChatRoleService chatRoleService; | 50 | private AiChatRoleService chatRoleService; |
| 50 | @Resource | 51 | @Resource |
| @@ -54,9 +55,9 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | @@ -54,9 +55,9 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | ||
| 54 | public Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId) { | 55 | public Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId) { |
| 55 | // 1.1 获得 AiChatRoleDO 聊天角色 | 56 | // 1.1 获得 AiChatRoleDO 聊天角色 |
| 56 | AiChatRoleDO role = createReqVO.getRoleId() != null ? chatRoleService.validateChatRole(createReqVO.getRoleId()) : null; | 57 | AiChatRoleDO role = createReqVO.getRoleId() != null ? chatRoleService.validateChatRole(createReqVO.getRoleId()) : null; |
| 57 | - // 1.2 获得 AiChatModelDO 聊天模型 | ||
| 58 | - AiChatModelDO model = role != null && role.getModelId() != null ? chatModalService.validateChatModel(role.getModelId()) | ||
| 59 | - : chatModalService.getRequiredDefaultChatModel(); | 58 | + // 1.2 获得 AiModelDO 聊天模型 |
| 59 | + AiModelDO model = role != null && role.getModelId() != null ? modalService.validateModel(role.getModelId()) | ||
| 60 | + : modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType()); | ||
| 60 | Assert.notNull(model, "必须找到默认模型"); | 61 | Assert.notNull(model, "必须找到默认模型"); |
| 61 | validateChatModel(model); | 62 | validateChatModel(model); |
| 62 | 63 | ||
| @@ -67,7 +68,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | @@ -67,7 +68,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | ||
| 67 | 68 | ||
| 68 | // 2. 创建 AiChatConversationDO 聊天对话 | 69 | // 2. 创建 AiChatConversationDO 聊天对话 |
| 69 | AiChatConversationDO conversation = new AiChatConversationDO().setUserId(userId).setPinned(false) | 70 | AiChatConversationDO conversation = new AiChatConversationDO().setUserId(userId).setPinned(false) |
| 70 | - .setModelId(model.getId()).setModel(model.getModel()).setKnowledgeId(createReqVO.getKnowledgeId()) | 71 | + .setModelId(model.getId()).setModel(model.getModel()) |
| 71 | .setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts()); | 72 | .setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts()); |
| 72 | if (role != null) { | 73 | if (role != null) { |
| 73 | conversation.setTitle(role.getName()).setRoleId(role.getId()).setSystemMessage(role.getSystemMessage()); | 74 | conversation.setTitle(role.getName()).setRoleId(role.getId()).setSystemMessage(role.getSystemMessage()); |
| @@ -86,9 +87,9 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | @@ -86,9 +87,9 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | ||
| 86 | throw exception(CHAT_CONVERSATION_NOT_EXISTS); | 87 | throw exception(CHAT_CONVERSATION_NOT_EXISTS); |
| 87 | } | 88 | } |
| 88 | // 1.2 校验模型是否存在(修改模型的情况) | 89 | // 1.2 校验模型是否存在(修改模型的情况) |
| 89 | - AiChatModelDO model = null; | 90 | + AiModelDO model = null; |
| 90 | if (updateReqVO.getModelId() != null) { | 91 | if (updateReqVO.getModelId() != null) { |
| 91 | - model = chatModalService.validateChatModel(updateReqVO.getModelId()); | 92 | + model = modalService.validateModel(updateReqVO.getModelId()); |
| 92 | } | 93 | } |
| 93 | 94 | ||
| 94 | // 1.3 校验知识库是否存在 | 95 | // 1.3 校验知识库是否存在 |
| @@ -139,10 +140,11 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | @@ -139,10 +140,11 @@ public class AiChatConversationServiceImpl implements AiChatConversationService | ||
| 139 | chatConversationMapper.deleteById(id); | 140 | chatConversationMapper.deleteById(id); |
| 140 | } | 141 | } |
| 141 | 142 | ||
| 142 | - private void validateChatModel(AiChatModelDO model) { | 143 | + private void validateChatModel(AiModelDO model) { |
| 143 | if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) { | 144 | if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) { |
| 144 | return; | 145 | return; |
| 145 | } | 146 | } |
| 147 | + Assert.equals(model.getType(), AiModelTypeEnum.CHAT.getType(), "模型类型不正确:" + model); | ||
| 146 | throw exception(CHAT_CONVERSATION_MODEL_ERROR); | 148 | throw exception(CHAT_CONVERSATION_MODEL_ERROR); |
| 147 | } | 149 | } |
| 148 | 150 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
| @@ -10,19 +10,24 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | @@ -10,19 +10,24 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 10 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 10 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 11 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | 11 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; |
| 12 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; | 12 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; |
| 13 | +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; | ||
| 13 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; | 14 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; |
| 14 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO; | 15 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO; |
| 15 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO; | ||
| 16 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; | 16 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; |
| 17 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; | 17 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; |
| 18 | -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; | ||
| 19 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | 18 | +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; |
| 19 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | ||
| 20 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; | ||
| 21 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; | ||
| 20 | import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper; | 22 | import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper; |
| 21 | -import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum; | ||
| 22 | import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants; | 23 | import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants; |
| 24 | +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; | ||
| 23 | import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; | 25 | import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; |
| 24 | -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; | ||
| 25 | -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; | 26 | +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO; |
| 27 | +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO; | ||
| 28 | +import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; | ||
| 29 | +import cn.iocoder.yudao.module.ai.service.model.AiModelService; | ||
| 30 | +import cn.iocoder.yudao.module.ai.service.model.AiToolService; | ||
| 26 | import jakarta.annotation.Resource; | 31 | import jakarta.annotation.Resource; |
| 27 | import lombok.extern.slf4j.Slf4j; | 32 | import lombok.extern.slf4j.Slf4j; |
| 28 | import org.springframework.ai.chat.messages.Message; | 33 | import org.springframework.ai.chat.messages.Message; |
| @@ -34,18 +39,19 @@ import org.springframework.ai.chat.model.ChatResponse; | @@ -34,18 +39,19 @@ import org.springframework.ai.chat.model.ChatResponse; | ||
| 34 | import org.springframework.ai.chat.model.StreamingChatModel; | 39 | import org.springframework.ai.chat.model.StreamingChatModel; |
| 35 | import org.springframework.ai.chat.prompt.ChatOptions; | 40 | import org.springframework.ai.chat.prompt.ChatOptions; |
| 36 | import org.springframework.ai.chat.prompt.Prompt; | 41 | import org.springframework.ai.chat.prompt.Prompt; |
| 37 | -import org.springframework.ai.chat.prompt.PromptTemplate; | ||
| 38 | import org.springframework.stereotype.Service; | 42 | import org.springframework.stereotype.Service; |
| 39 | import org.springframework.transaction.annotation.Transactional; | 43 | import org.springframework.transaction.annotation.Transactional; |
| 40 | import reactor.core.publisher.Flux; | 44 | import reactor.core.publisher.Flux; |
| 41 | 45 | ||
| 42 | import java.time.LocalDateTime; | 46 | import java.time.LocalDateTime; |
| 43 | import java.util.*; | 47 | import java.util.*; |
| 48 | +import java.util.stream.Collectors; | ||
| 44 | 49 | ||
| 45 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 50 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 46 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; | 51 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; |
| 47 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 52 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 48 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | 53 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| 54 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | ||
| 49 | import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; | 55 | import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; |
| 50 | import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_NOT_EXIST; | 56 | import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_NOT_EXIST; |
| 51 | 57 | ||
| @@ -58,138 +64,199 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_N | @@ -58,138 +64,199 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_N | ||
| 58 | @Slf4j | 64 | @Slf4j |
| 59 | public class AiChatMessageServiceImpl implements AiChatMessageService { | 65 | public class AiChatMessageServiceImpl implements AiChatMessageService { |
| 60 | 66 | ||
| 67 | + /** | ||
| 68 | + * 知识库转 {@link UserMessage} 的内容模版 | ||
| 69 | + */ | ||
| 70 | + private static final String KNOWLEDGE_USER_MESSAGE_TEMPLATE = "使用 <Reference></Reference> 标记中的内容作为本次对话的参考:\n\n" + | ||
| 71 | + "%s\n\n" + // 多个 <Reference></Reference> 的拼接 | ||
| 72 | + "回答要求:\n- 避免提及你是从 <Reference></Reference> 获取的知识。"; | ||
| 73 | + | ||
| 61 | @Resource | 74 | @Resource |
| 62 | private AiChatMessageMapper chatMessageMapper; | 75 | private AiChatMessageMapper chatMessageMapper; |
| 63 | 76 | ||
| 64 | @Resource | 77 | @Resource |
| 65 | private AiChatConversationService chatConversationService; | 78 | private AiChatConversationService chatConversationService; |
| 66 | @Resource | 79 | @Resource |
| 67 | - private AiChatModelService chatModalService; | 80 | + private AiChatRoleService chatRoleService; |
| 68 | @Resource | 81 | @Resource |
| 69 | - private AiApiKeyService apiKeyService; | 82 | + private AiModelService modalService; |
| 70 | @Resource | 83 | @Resource |
| 71 | private AiKnowledgeSegmentService knowledgeSegmentService; | 84 | private AiKnowledgeSegmentService knowledgeSegmentService; |
| 85 | + @Resource | ||
| 86 | + private AiKnowledgeDocumentService knowledgeDocumentService; | ||
| 87 | + @Resource | ||
| 88 | + private AiToolService toolService; | ||
| 72 | 89 | ||
| 73 | @Transactional(rollbackFor = Exception.class) | 90 | @Transactional(rollbackFor = Exception.class) |
| 74 | public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) { | 91 | public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) { |
| 75 | // 1.1 校验对话存在 | 92 | // 1.1 校验对话存在 |
| 76 | - AiChatConversationDO conversation = chatConversationService.validateChatConversationExists(sendReqVO.getConversationId()); | 93 | + AiChatConversationDO conversation = chatConversationService |
| 94 | + .validateChatConversationExists(sendReqVO.getConversationId()); | ||
| 77 | if (ObjUtil.notEqual(conversation.getUserId(), userId)) { | 95 | if (ObjUtil.notEqual(conversation.getUserId(), userId)) { |
| 78 | throw exception(CHAT_CONVERSATION_NOT_EXISTS); | 96 | throw exception(CHAT_CONVERSATION_NOT_EXISTS); |
| 79 | } | 97 | } |
| 80 | List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId()); | 98 | List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId()); |
| 81 | // 1.2 校验模型 | 99 | // 1.2 校验模型 |
| 82 | - AiChatModelDO model = chatModalService.validateChatModel(conversation.getModelId()); | ||
| 83 | - ChatModel chatModel = apiKeyService.getChatModel(model.getKeyId()); | 100 | + AiModelDO model = modalService.validateModel(conversation.getModelId()); |
| 101 | + ChatModel chatModel = modalService.getChatModel(model.getId()); | ||
| 102 | + | ||
| 103 | + // 2. 知识库找回 | ||
| 104 | + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(), | ||
| 105 | + conversation); | ||
| 84 | 106 | ||
| 85 | - // 2. 插入 user 发送消息 | 107 | + // 3. 插入 user 发送消息 |
| 86 | AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model, | 108 | AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model, |
| 87 | - userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext()); | 109 | + userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(), |
| 110 | + null); | ||
| 88 | 111 | ||
| 89 | // 3.1 插入 assistant 接收消息 | 112 | // 3.1 插入 assistant 接收消息 |
| 90 | AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model, | 113 | AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model, |
| 91 | - userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext()); | ||
| 92 | - | ||
| 93 | - // 3.2 召回段落 | ||
| 94 | - List<AiKnowledgeSegmentDO> segmentList = recallSegment(sendReqVO.getContent(), conversation.getKnowledgeId()); | 114 | + userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(), |
| 115 | + knowledgeSegments); | ||
| 95 | 116 | ||
| 96 | - // 3.3 创建 chat 需要的 Prompt | ||
| 97 | - Prompt prompt = buildPrompt(conversation, historyMessages, segmentList, model, sendReqVO); | 117 | + // 3.2 创建 chat 需要的 Prompt |
| 118 | + Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO); | ||
| 98 | ChatResponse chatResponse = chatModel.call(prompt); | 119 | ChatResponse chatResponse = chatModel.call(prompt); |
| 99 | 120 | ||
| 100 | - // 3.4 段式返回 | ||
| 101 | - String newContent = chatResponse.getResult().getOutput().getContent(); | ||
| 102 | - chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId)).setContent(newContent)); | ||
| 103 | - return new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) | ||
| 104 | - .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent)); | 121 | + // 3.3 更新响应内容 |
| 122 | + String newContent = chatResponse.getResult().getOutput().getText(); | ||
| 123 | + chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(newContent)); | ||
| 124 | + // 3.4 响应结果 | ||
| 125 | + List<AiChatMessageRespVO.KnowledgeSegment> segments = BeanUtils.toBean(knowledgeSegments, | ||
| 126 | + AiChatMessageRespVO.KnowledgeSegment.class, | ||
| 127 | + segment -> { | ||
| 128 | + AiKnowledgeDocumentDO document = knowledgeDocumentService | ||
| 129 | + .getKnowledgeDocument(segment.getDocumentId()); | ||
| 130 | + segment.setDocumentName(document != null ? document.getName() : null); | ||
| 131 | + }); | ||
| 132 | + return new AiChatMessageSendRespVO() | ||
| 133 | + .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) | ||
| 134 | + .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class) | ||
| 135 | + .setContent(newContent).setSegments(segments)); | ||
| 105 | } | 136 | } |
| 106 | 137 | ||
| 107 | @Override | 138 | @Override |
| 108 | - public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId) { | 139 | + public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, |
| 140 | + Long userId) { | ||
| 109 | // 1.1 校验对话存在 | 141 | // 1.1 校验对话存在 |
| 110 | - AiChatConversationDO conversation = chatConversationService.validateChatConversationExists(sendReqVO.getConversationId()); | 142 | + AiChatConversationDO conversation = chatConversationService |
| 143 | + .validateChatConversationExists(sendReqVO.getConversationId()); | ||
| 111 | if (ObjUtil.notEqual(conversation.getUserId(), userId)) { | 144 | if (ObjUtil.notEqual(conversation.getUserId(), userId)) { |
| 112 | throw exception(CHAT_CONVERSATION_NOT_EXISTS); | 145 | throw exception(CHAT_CONVERSATION_NOT_EXISTS); |
| 113 | } | 146 | } |
| 114 | List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId()); | 147 | List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId()); |
| 115 | // 1.2 校验模型 | 148 | // 1.2 校验模型 |
| 116 | - AiChatModelDO model = chatModalService.validateChatModel(conversation.getModelId()); | ||
| 117 | - StreamingChatModel chatModel = apiKeyService.getChatModel(model.getKeyId()); | 149 | + AiModelDO model = modalService.validateModel(conversation.getModelId()); |
| 150 | + StreamingChatModel chatModel = modalService.getChatModel(model.getId()); | ||
| 118 | 151 | ||
| 119 | - // 2. 插入 user 发送消息 | 152 | + // 2. 知识库找回 |
| 153 | + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(), | ||
| 154 | + conversation); | ||
| 155 | + | ||
| 156 | + // 3. 插入 user 发送消息 | ||
| 120 | AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model, | 157 | AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model, |
| 121 | - userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext()); | 158 | + userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(), |
| 159 | + null); | ||
| 122 | 160 | ||
| 123 | - // 3.1 插入 assistant 接收消息 | 161 | + // 4.1 插入 assistant 接收消息 |
| 124 | AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model, | 162 | AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model, |
| 125 | - userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext()); | ||
| 126 | - | ||
| 127 | - | ||
| 128 | - // 3.2 召回段落 | ||
| 129 | - List<AiKnowledgeSegmentDO> segmentList = recallSegment(sendReqVO.getContent(), conversation.getKnowledgeId()); | 163 | + userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(), |
| 164 | + knowledgeSegments); | ||
| 130 | 165 | ||
| 131 | - // 3.3 构建 Prompt,并进行调用 | ||
| 132 | - Prompt prompt = buildPrompt(conversation, historyMessages, segmentList, model, sendReqVO); | 166 | + // 4.2 构建 Prompt,并进行调用 |
| 167 | + Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO); | ||
| 133 | Flux<ChatResponse> streamResponse = chatModel.stream(prompt); | 168 | Flux<ChatResponse> streamResponse = chatModel.stream(prompt); |
| 134 | 169 | ||
| 135 | - // 3.4 流式返回 | ||
| 136 | - // TODO 注意:Schedulers.immediate() 目的是,避免默认 Schedulers.parallel() 并发消费 chunk 导致 SSE 响应前端会乱序问题 | 170 | + // 4.3 流式返回 |
| 137 | StringBuffer contentBuffer = new StringBuffer(); | 171 | StringBuffer contentBuffer = new StringBuffer(); |
| 138 | return streamResponse.map(chunk -> { | 172 | return streamResponse.map(chunk -> { |
| 139 | - String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null; | 173 | + // 处理知识库的返回,只有首次才有 |
| 174 | + List<AiChatMessageRespVO.KnowledgeSegment> segments = null; | ||
| 175 | + if (StrUtil.isEmpty(contentBuffer)) { | ||
| 176 | + segments = BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class, | ||
| 177 | + segment -> TenantUtils.executeIgnore(() -> { | ||
| 178 | + AiKnowledgeDocumentDO document = knowledgeDocumentService | ||
| 179 | + .getKnowledgeDocument(segment.getDocumentId()); | ||
| 180 | + segment.setDocumentName(document != null ? document.getName() : null); | ||
| 181 | + })); | ||
| 182 | + } | ||
| 183 | + // 响应结果 | ||
| 184 | + String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null; | ||
| 140 | newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况 | 185 | newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况 |
| 141 | contentBuffer.append(newContent); | 186 | contentBuffer.append(newContent); |
| 142 | - // 响应结果 | ||
| 143 | - return success(new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) | ||
| 144 | - .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent))); | 187 | + return success(new AiChatMessageSendRespVO() |
| 188 | + .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) | ||
| 189 | + .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class) | ||
| 190 | + .setContent(newContent).setSegments(segments))); | ||
| 145 | }).doOnComplete(() -> { | 191 | }).doOnComplete(() -> { |
| 146 | // 忽略租户,因为 Flux 异步无法透传租户 | 192 | // 忽略租户,因为 Flux 异步无法透传租户 |
| 147 | - TenantUtils.executeIgnore(() -> | ||
| 148 | - chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId)) | ||
| 149 | - .setContent(contentBuffer.toString()))); | 193 | + TenantUtils.executeIgnore(() -> chatMessageMapper.updateById( |
| 194 | + new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString()))); | ||
| 150 | }).doOnError(throwable -> { | 195 | }).doOnError(throwable -> { |
| 151 | log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable); | 196 | log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable); |
| 152 | // 忽略租户,因为 Flux 异步无法透传租户 | 197 | // 忽略租户,因为 Flux 异步无法透传租户 |
| 153 | - TenantUtils.executeIgnore(() -> | ||
| 154 | - chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage()))); | 198 | + TenantUtils.executeIgnore(() -> chatMessageMapper.updateById( |
| 199 | + new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage()))); | ||
| 155 | }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR))); | 200 | }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR))); |
| 156 | } | 201 | } |
| 157 | 202 | ||
| 158 | - private List<AiKnowledgeSegmentDO> recallSegment(String content, Long knowledgeId) { | ||
| 159 | - if (Objects.isNull(knowledgeId)) { | 203 | + private List<AiKnowledgeSegmentSearchRespBO> recallKnowledgeSegment(String content, |
| 204 | + AiChatConversationDO conversation) { | ||
| 205 | + // 1. 查询聊天角色 | ||
| 206 | + if (conversation == null || conversation.getRoleId() == null) { | ||
| 207 | + return Collections.emptyList(); | ||
| 208 | + } | ||
| 209 | + AiChatRoleDO role = chatRoleService.getChatRole(conversation.getRoleId()); | ||
| 210 | + if (role == null || CollUtil.isEmpty(role.getKnowledgeIds())) { | ||
| 160 | return Collections.emptyList(); | 211 | return Collections.emptyList(); |
| 161 | } | 212 | } |
| 162 | - return knowledgeSegmentService.similaritySearch(new AiKnowledgeSegmentSearchReqVO().setKnowledgeId(knowledgeId).setContent(content)); | ||
| 163 | - } | ||
| 164 | - | ||
| 165 | - private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages,List<AiKnowledgeSegmentDO> segmentList, | ||
| 166 | - AiChatModelDO model, AiChatMessageSendReqVO sendReqVO) { | ||
| 167 | - // 1. 构建 Prompt Message 列表 | ||
| 168 | - List<Message> chatMessages = new ArrayList<>(); | ||
| 169 | 213 | ||
| 170 | - // 1.1 召回内容消息构建 | ||
| 171 | - if (CollUtil.isNotEmpty(segmentList)) { | ||
| 172 | - PromptTemplate promptTemplate = new PromptTemplate(AiChatRoleEnum.AI_KNOWLEDGE_ROLE.getSystemMessage()); | ||
| 173 | - StringBuilder infoBuilder = StrUtil.builder(); | ||
| 174 | - segmentList.forEach(segment -> infoBuilder.append(System.lineSeparator()).append(segment.getContent())); | ||
| 175 | - Message message = promptTemplate.createMessage(Map.of("info", infoBuilder.toString())); | ||
| 176 | - chatMessages.add(message); | 214 | + // 2. 遍历找回 |
| 215 | + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = new ArrayList<>(); | ||
| 216 | + for (Long knowledgeId : role.getKnowledgeIds()) { | ||
| 217 | + knowledgeSegments.addAll(knowledgeSegmentService.searchKnowledgeSegment(new AiKnowledgeSegmentSearchReqBO() | ||
| 218 | + .setKnowledgeId(knowledgeId).setContent(content))); | ||
| 177 | } | 219 | } |
| 220 | + return knowledgeSegments; | ||
| 221 | + } | ||
| 178 | 222 | ||
| 179 | - // 1.2 system context 角色设定 | 223 | + private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages, |
| 224 | + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments, | ||
| 225 | + AiModelDO model, AiChatMessageSendReqVO sendReqVO) { | ||
| 226 | + List<Message> chatMessages = new ArrayList<>(); | ||
| 227 | + // 1.1 System Context 角色设定 | ||
| 180 | if (StrUtil.isNotBlank(conversation.getSystemMessage())) { | 228 | if (StrUtil.isNotBlank(conversation.getSystemMessage())) { |
| 181 | chatMessages.add(new SystemMessage(conversation.getSystemMessage())); | 229 | chatMessages.add(new SystemMessage(conversation.getSystemMessage())); |
| 182 | } | 230 | } |
| 183 | - // 1.3 history message 历史消息 | 231 | + |
| 232 | + // 1.2 历史 history message 历史消息 | ||
| 184 | List<AiChatMessageDO> contextMessages = filterContextMessages(messages, conversation, sendReqVO); | 233 | List<AiChatMessageDO> contextMessages = filterContextMessages(messages, conversation, sendReqVO); |
| 185 | - contextMessages.forEach(message -> chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent()))); | ||
| 186 | - // 1.4 user message 新发送消息 | 234 | + contextMessages |
| 235 | + .forEach(message -> chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent()))); | ||
| 236 | + | ||
| 237 | + // 1.3 当前 user message 新发送消息 | ||
| 187 | chatMessages.add(new UserMessage(sendReqVO.getContent())); | 238 | chatMessages.add(new UserMessage(sendReqVO.getContent())); |
| 188 | 239 | ||
| 189 | - // 2. 构建 ChatOptions 对象 | 240 | + // 1.4 知识库,通过 UserMessage 实现 |
| 241 | + if (CollUtil.isNotEmpty(knowledgeSegments)) { | ||
| 242 | + String reference = knowledgeSegments.stream() | ||
| 243 | + .map(segment -> "<Reference>" + segment.getContent() + "</Reference>") | ||
| 244 | + .collect(Collectors.joining("\n\n")); | ||
| 245 | + chatMessages.add(new UserMessage(String.format(KNOWLEDGE_USER_MESSAGE_TEMPLATE, reference))); | ||
| 246 | + } | ||
| 247 | + | ||
| 248 | + // 2.1 查询 tool 工具 | ||
| 249 | + Set<String> toolNames = null; | ||
| 250 | + if (conversation.getRoleId() != null) { | ||
| 251 | + AiChatRoleDO chatRole = chatRoleService.getChatRole(conversation.getRoleId()); | ||
| 252 | + if (chatRole != null && CollUtil.isNotEmpty(chatRole.getToolIds())) { | ||
| 253 | + toolNames = convertSet(toolService.getToolList(chatRole.getToolIds()), AiToolDO::getName); | ||
| 254 | + } | ||
| 255 | + } | ||
| 256 | + // 2.2 构建 ChatOptions 对象 | ||
| 190 | AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); | 257 | AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); |
| 191 | ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), | 258 | ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), |
| 192 | - conversation.getTemperature(), conversation.getMaxTokens()); | 259 | + conversation.getTemperature(), conversation.getMaxTokens(), toolNames); |
| 193 | return new Prompt(chatMessages, chatOptions); | 260 | return new Prompt(chatMessages, chatOptions); |
| 194 | } | 261 | } |
| 195 | 262 | ||
| @@ -204,8 +271,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | @@ -204,8 +271,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | ||
| 204 | * @return 消息上下文 | 271 | * @return 消息上下文 |
| 205 | */ | 272 | */ |
| 206 | private List<AiChatMessageDO> filterContextMessages(List<AiChatMessageDO> messages, | 273 | private List<AiChatMessageDO> filterContextMessages(List<AiChatMessageDO> messages, |
| 207 | - AiChatConversationDO conversation, | ||
| 208 | - AiChatMessageSendReqVO sendReqVO) { | 274 | + AiChatConversationDO conversation, |
| 275 | + AiChatMessageSendReqVO sendReqVO) { | ||
| 209 | if (conversation.getMaxContexts() == null || ObjUtil.notEqual(sendReqVO.getUseContext(), Boolean.TRUE)) { | 276 | if (conversation.getMaxContexts() == null || ObjUtil.notEqual(sendReqVO.getUseContext(), Boolean.TRUE)) { |
| 210 | return Collections.emptyList(); | 277 | return Collections.emptyList(); |
| 211 | } | 278 | } |
| @@ -216,7 +283,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | @@ -216,7 +283,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | ||
| 216 | continue; | 283 | continue; |
| 217 | } | 284 | } |
| 218 | AiChatMessageDO userMessage = CollUtil.get(messages, i - 1); | 285 | AiChatMessageDO userMessage = CollUtil.get(messages, i - 1); |
| 219 | - if (userMessage == null || ObjUtil.notEqual(assistantMessage.getReplyId(), userMessage.getId()) | 286 | + if (userMessage == null |
| 287 | + || ObjUtil.notEqual(assistantMessage.getReplyId(), userMessage.getId()) | ||
| 220 | || StrUtil.isEmpty(assistantMessage.getContent())) { | 288 | || StrUtil.isEmpty(assistantMessage.getContent())) { |
| 221 | continue; | 289 | continue; |
| 222 | } | 290 | } |
| @@ -233,11 +301,13 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | @@ -233,11 +301,13 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | ||
| 233 | } | 301 | } |
| 234 | 302 | ||
| 235 | private AiChatMessageDO createChatMessage(Long conversationId, Long replyId, | 303 | private AiChatMessageDO createChatMessage(Long conversationId, Long replyId, |
| 236 | - AiChatModelDO model, Long userId, Long roleId, | ||
| 237 | - MessageType messageType, String content, Boolean useContext) { | 304 | + AiModelDO model, Long userId, Long roleId, |
| 305 | + MessageType messageType, String content, Boolean useContext, | ||
| 306 | + List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments) { | ||
| 238 | AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId) | 307 | AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId) |
| 239 | .setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId) | 308 | .setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId) |
| 240 | - .setType(messageType.getValue()).setContent(content).setUseContext(useContext); | 309 | + .setType(messageType.getValue()).setContent(content).setUseContext(useContext) |
| 310 | + .setSegmentIds(convertList(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getId)); | ||
| 241 | message.setCreateTime(LocalDateTime.now()); | 311 | message.setCreateTime(LocalDateTime.now()); |
| 242 | chatMessageMapper.insert(message); | 312 | chatMessageMapper.insert(message); |
| 243 | return message; | 313 | return message; |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
| @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.service.image; | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.service.image; | ||
| 3 | import cn.hutool.core.bean.BeanUtil; | 3 | import cn.hutool.core.bean.BeanUtil; |
| 4 | import cn.hutool.core.codec.Base64; | 4 | import cn.hutool.core.codec.Base64; |
| 5 | import cn.hutool.core.collection.CollUtil; | 5 | import cn.hutool.core.collection.CollUtil; |
| 6 | +import cn.hutool.core.lang.Assert; | ||
| 6 | import cn.hutool.core.map.MapUtil; | 7 | import cn.hutool.core.map.MapUtil; |
| 7 | import cn.hutool.core.util.ObjUtil; | 8 | import cn.hutool.core.util.ObjUtil; |
| 8 | import cn.hutool.core.util.StrUtil; | 9 | import cn.hutool.core.util.StrUtil; |
| @@ -12,15 +13,19 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | @@ -12,15 +13,19 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | ||
| 12 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | 13 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; |
| 13 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 14 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 14 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 15 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 15 | -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.*; | 16 | +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO; |
| 17 | +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePageReqVO; | ||
| 18 | +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePublicPageReqVO; | ||
| 19 | +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageUpdateReqVO; | ||
| 16 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO; | 20 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO; |
| 17 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO; | 21 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO; |
| 18 | import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; | 22 | import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; |
| 23 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; | ||
| 19 | import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; | 24 | import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; |
| 20 | import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; | 25 | import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; |
| 21 | -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; | 26 | +import cn.iocoder.yudao.module.ai.service.model.AiModelService; |
| 22 | import cn.iocoder.yudao.module.infra.api.file.FileApi; | 27 | import cn.iocoder.yudao.module.infra.api.file.FileApi; |
| 23 | -import com.alibaba.cloud.ai.tongyi.image.TongYiImagesOptions; | 28 | +import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions; |
| 24 | import jakarta.annotation.Resource; | 29 | import jakarta.annotation.Resource; |
| 25 | import lombok.extern.slf4j.Slf4j; | 30 | import lombok.extern.slf4j.Slf4j; |
| 26 | import org.springframework.ai.image.ImageModel; | 31 | import org.springframework.ai.image.ImageModel; |
| @@ -55,13 +60,13 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; | @@ -55,13 +60,13 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; | ||
| 55 | public class AiImageServiceImpl implements AiImageService { | 60 | public class AiImageServiceImpl implements AiImageService { |
| 56 | 61 | ||
| 57 | @Resource | 62 | @Resource |
| 58 | - private AiImageMapper imageMapper; | 63 | + private AiModelService modelService; |
| 59 | 64 | ||
| 60 | @Resource | 65 | @Resource |
| 61 | - private FileApi fileApi; | 66 | + private AiImageMapper imageMapper; |
| 62 | 67 | ||
| 63 | @Resource | 68 | @Resource |
| 64 | - private AiApiKeyService apiKeyService; | 69 | + private FileApi fileApi; |
| 65 | 70 | ||
| 66 | @Override | 71 | @Override |
| 67 | public PageResult<AiImageDO> getImagePageMy(Long userId, AiImagePageReqVO pageReqVO) { | 72 | public PageResult<AiImageDO> getImagePageMy(Long userId, AiImagePageReqVO pageReqVO) { |
| @@ -88,23 +93,31 @@ public class AiImageServiceImpl implements AiImageService { | @@ -88,23 +93,31 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 88 | 93 | ||
| 89 | @Override | 94 | @Override |
| 90 | public Long drawImage(Long userId, AiImageDrawReqVO drawReqVO) { | 95 | public Long drawImage(Long userId, AiImageDrawReqVO drawReqVO) { |
| 91 | - // 1. 保存数据库 | ||
| 92 | - AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false) | ||
| 93 | - .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); | 96 | + // 1. 校验模型 |
| 97 | + AiModelDO model = modelService.validateModel(drawReqVO.getModelId()); | ||
| 98 | + | ||
| 99 | + // 2. 保存数据库 | ||
| 100 | + AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId) | ||
| 101 | + .setPlatform(model.getPlatform()).setModelId(model.getId()).setModel(model.getModel()) | ||
| 102 | + .setPublicStatus(false).setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); | ||
| 94 | imageMapper.insert(image); | 103 | imageMapper.insert(image); |
| 95 | - // 2. 异步绘制,后续前端通过返回的 id 进行轮询结果 | ||
| 96 | - getSelf().executeDrawImage(image, drawReqVO); | 104 | + |
| 105 | + // 3. 异步绘制,后续前端通过返回的 id 进行轮询结果 | ||
| 106 | + getSelf().executeDrawImage(image, drawReqVO, model); | ||
| 97 | return image.getId(); | 107 | return image.getId(); |
| 98 | } | 108 | } |
| 99 | 109 | ||
| 100 | @Async | 110 | @Async |
| 101 | - public void executeDrawImage(AiImageDO image, AiImageDrawReqVO req) { | 111 | + public void executeDrawImage(AiImageDO image, AiImageDrawReqVO reqVO, AiModelDO model) { |
| 102 | try { | 112 | try { |
| 103 | // 1.1 构建请求 | 113 | // 1.1 构建请求 |
| 104 | - ImageOptions request = buildImageOptions(req); | 114 | + ImageOptions request = buildImageOptions(reqVO, model); |
| 105 | // 1.2 执行请求 | 115 | // 1.2 执行请求 |
| 106 | - ImageModel imageModel = apiKeyService.getImageModel(AiPlatformEnum.validatePlatform(req.getPlatform())); | ||
| 107 | - ImageResponse response = imageModel.call(new ImagePrompt(req.getPrompt(), request)); | 116 | + ImageModel imageModel = modelService.getImageModel(model.getId()); |
| 117 | + ImageResponse response = imageModel.call(new ImagePrompt(reqVO.getPrompt(), request)); | ||
| 118 | + if (response.getResult() == null) { | ||
| 119 | + throw new IllegalArgumentException("生成结果为空"); | ||
| 120 | + } | ||
| 108 | 121 | ||
| 109 | // 2. 上传到文件服务 | 122 | // 2. 上传到文件服务 |
| 110 | String b64Json = response.getResult().getOutput().getB64Json(); | 123 | String b64Json = response.getResult().getOutput().getB64Json(); |
| @@ -116,49 +129,49 @@ public class AiImageServiceImpl implements AiImageService { | @@ -116,49 +129,49 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 116 | imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(AiImageStatusEnum.SUCCESS.getStatus()) | 129 | imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(AiImageStatusEnum.SUCCESS.getStatus()) |
| 117 | .setPicUrl(filePath).setFinishTime(LocalDateTime.now())); | 130 | .setPicUrl(filePath).setFinishTime(LocalDateTime.now())); |
| 118 | } catch (Exception ex) { | 131 | } catch (Exception ex) { |
| 119 | - log.error("[doDall][image({}) 生成异常]", image, ex); | 132 | + log.error("[executeDrawImage][image({}) 生成异常]", image, ex); |
| 120 | imageMapper.updateById(new AiImageDO().setId(image.getId()) | 133 | imageMapper.updateById(new AiImageDO().setId(image.getId()) |
| 121 | .setStatus(AiImageStatusEnum.FAIL.getStatus()) | 134 | .setStatus(AiImageStatusEnum.FAIL.getStatus()) |
| 122 | .setErrorMessage(ex.getMessage()).setFinishTime(LocalDateTime.now())); | 135 | .setErrorMessage(ex.getMessage()).setFinishTime(LocalDateTime.now())); |
| 123 | } | 136 | } |
| 124 | } | 137 | } |
| 125 | 138 | ||
| 126 | - private static ImageOptions buildImageOptions(AiImageDrawReqVO draw) { | ||
| 127 | - if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) { | 139 | + private static ImageOptions buildImageOptions(AiImageDrawReqVO draw, AiModelDO model) { |
| 140 | + if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) { | ||
| 128 | // https://platform.openai.com/docs/api-reference/images/create | 141 | // https://platform.openai.com/docs/api-reference/images/create |
| 129 | - return OpenAiImageOptions.builder().withModel(draw.getModel()) | 142 | + return OpenAiImageOptions.builder().withModel(model.getModel()) |
| 130 | .withHeight(draw.getHeight()).withWidth(draw.getWidth()) | 143 | .withHeight(draw.getHeight()).withWidth(draw.getWidth()) |
| 131 | .withStyle(MapUtil.getStr(draw.getOptions(), "style")) // 风格 | 144 | .withStyle(MapUtil.getStr(draw.getOptions(), "style")) // 风格 |
| 132 | .withResponseFormat("b64_json") | 145 | .withResponseFormat("b64_json") |
| 133 | .build(); | 146 | .build(); |
| 134 | - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) { | 147 | + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) { |
| 135 | // https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage | 148 | // https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage |
| 136 | // https://platform.stability.ai/docs/api-reference#tag/Text-to-Image/operation/textToImage | 149 | // https://platform.stability.ai/docs/api-reference#tag/Text-to-Image/operation/textToImage |
| 137 | - return StabilityAiImageOptions.builder().withModel(draw.getModel()) | ||
| 138 | - .withHeight(draw.getHeight()).withWidth(draw.getWidth()) | ||
| 139 | - .withSeed(Long.valueOf(draw.getOptions().get("seed"))) | ||
| 140 | - .withCfgScale(Float.valueOf(draw.getOptions().get("scale"))) | ||
| 141 | - .withSteps(Integer.valueOf(draw.getOptions().get("steps"))) | ||
| 142 | - .withSampler(String.valueOf(draw.getOptions().get("sampler"))) | ||
| 143 | - .withStylePreset(String.valueOf(draw.getOptions().get("stylePreset"))) | ||
| 144 | - .withClipGuidancePreset(String.valueOf(draw.getOptions().get("clipGuidancePreset"))) | 150 | + return StabilityAiImageOptions.builder().model(model.getModel()) |
| 151 | + .height(draw.getHeight()).width(draw.getWidth()) | ||
| 152 | + .seed(Long.valueOf(draw.getOptions().get("seed"))) | ||
| 153 | + .cfgScale(Float.valueOf(draw.getOptions().get("scale"))) | ||
| 154 | + .steps(Integer.valueOf(draw.getOptions().get("steps"))) | ||
| 155 | + .sampler(String.valueOf(draw.getOptions().get("sampler"))) | ||
| 156 | + .stylePreset(String.valueOf(draw.getOptions().get("stylePreset"))) | ||
| 157 | + .clipGuidancePreset(String.valueOf(draw.getOptions().get("clipGuidancePreset"))) | ||
| 145 | .build(); | 158 | .build(); |
| 146 | - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) { | ||
| 147 | - return TongYiImagesOptions.builder() | ||
| 148 | - .withModel(draw.getModel()).withN(1) | 159 | + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) { |
| 160 | + return DashScopeImageOptions.builder() | ||
| 161 | + .withModel(model.getModel()).withN(1) | ||
| 149 | .withHeight(draw.getHeight()).withWidth(draw.getWidth()) | 162 | .withHeight(draw.getHeight()).withWidth(draw.getWidth()) |
| 150 | .build(); | 163 | .build(); |
| 151 | - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) { | 164 | + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) { |
| 152 | return QianFanImageOptions.builder() | 165 | return QianFanImageOptions.builder() |
| 153 | - .withModel(draw.getModel()).withN(1) | ||
| 154 | - .withHeight(draw.getHeight()).withWidth(draw.getWidth()) | 166 | + .model(model.getModel()).N(1) |
| 167 | + .height(draw.getHeight()).width(draw.getWidth()) | ||
| 155 | .build(); | 168 | .build(); |
| 156 | - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) { | 169 | + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) { |
| 157 | return ZhiPuAiImageOptions.builder() | 170 | return ZhiPuAiImageOptions.builder() |
| 158 | - .withModel(draw.getModel()) | 171 | + .model(model.getModel()) |
| 159 | .build(); | 172 | .build(); |
| 160 | } | 173 | } |
| 161 | - throw new IllegalArgumentException("不支持的 AI 平台:" + draw.getPlatform()); | 174 | + throw new IllegalArgumentException("不支持的 AI 平台:" + model.getPlatform()); |
| 162 | } | 175 | } |
| 163 | 176 | ||
| 164 | @Override | 177 | @Override |
| @@ -205,52 +218,56 @@ public class AiImageServiceImpl implements AiImageService { | @@ -205,52 +218,56 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 205 | 218 | ||
| 206 | @Override | 219 | @Override |
| 207 | @Transactional(rollbackFor = Exception.class) | 220 | @Transactional(rollbackFor = Exception.class) |
| 208 | - public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO reqVO) { | ||
| 209 | - MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi(); | ||
| 210 | - // 1. 保存数据库 | ||
| 211 | - AiImageDO image = BeanUtils.toBean(reqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false) | 221 | + public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO drawReqVO) { |
| 222 | + // 1. 校验模型 | ||
| 223 | + AiModelDO model = modelService.validateModel(drawReqVO.getModelId()); | ||
| 224 | + Assert.equals(model.getPlatform(), AiPlatformEnum.MIDJOURNEY.getPlatform(), "平台不匹配"); | ||
| 225 | + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(model.getId()); | ||
| 226 | + | ||
| 227 | + // 2. 保存数据库 | ||
| 228 | + AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false) | ||
| 212 | .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()) | 229 | .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()) |
| 213 | - .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()); | 230 | + .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()).setModelId(model.getId()).setModel(model.getName()); |
| 214 | imageMapper.insert(image); | 231 | imageMapper.insert(image); |
| 215 | 232 | ||
| 216 | - // 2. 调用 Midjourney Proxy 提交任务 | ||
| 217 | - List<String> base64Array = StrUtil.isBlank(reqVO.getReferImageUrl()) ? null : | ||
| 218 | - Collections.singletonList("data:image/jpeg;base64,".concat(Base64.encode(HttpUtil.downloadBytes(reqVO.getReferImageUrl())))); | 233 | + // 3. 调用 Midjourney Proxy 提交任务 |
| 234 | + List<String> base64Array = StrUtil.isBlank(drawReqVO.getReferImageUrl()) ? null : | ||
| 235 | + Collections.singletonList("data:image/jpeg;base64,".concat(Base64.encode(HttpUtil.downloadBytes(drawReqVO.getReferImageUrl())))); | ||
| 219 | MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest( | 236 | MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest( |
| 220 | - base64Array, reqVO.getPrompt(),null, | ||
| 221 | - MidjourneyApi.ImagineRequest.buildState(reqVO.getWidth(), | ||
| 222 | - reqVO.getHeight(), reqVO.getVersion(), reqVO.getModel())); | 237 | + base64Array, drawReqVO.getPrompt(),null, |
| 238 | + MidjourneyApi.ImagineRequest.buildState(drawReqVO.getWidth(), | ||
| 239 | + drawReqVO.getHeight(), drawReqVO.getVersion(), model.getModel())); | ||
| 223 | MidjourneyApi.SubmitResponse imagineResponse = midjourneyApi.imagine(imagineRequest); | 240 | MidjourneyApi.SubmitResponse imagineResponse = midjourneyApi.imagine(imagineRequest); |
| 224 | 241 | ||
| 225 | - // 3. 情况一【失败】:抛出业务异常 | 242 | + // 4.1 情况一【失败】:抛出业务异常 |
| 226 | if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(imagineResponse.code())) { | 243 | if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(imagineResponse.code())) { |
| 227 | String description = imagineResponse.description().contains("quota_not_enough") ? | 244 | String description = imagineResponse.description().contains("quota_not_enough") ? |
| 228 | "账户余额不足" : imagineResponse.description(); | 245 | "账户余额不足" : imagineResponse.description(); |
| 229 | throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description); | 246 | throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description); |
| 230 | } | 247 | } |
| 231 | 248 | ||
| 232 | - // 4. 情况二【成功】:更新 taskId 和参数 | 249 | + // 4.2 情况二【成功】:更新 taskId 和参数 |
| 233 | imageMapper.updateById(new AiImageDO().setId(image.getId()) | 250 | imageMapper.updateById(new AiImageDO().setId(image.getId()) |
| 234 | - .setTaskId(imagineResponse.result()).setOptions(BeanUtil.beanToMap(reqVO))); | 251 | + .setTaskId(imagineResponse.result()).setOptions(BeanUtil.beanToMap(drawReqVO))); |
| 235 | return image.getId(); | 252 | return image.getId(); |
| 236 | } | 253 | } |
| 237 | 254 | ||
| 238 | @Override | 255 | @Override |
| 239 | public Integer midjourneySync() { | 256 | public Integer midjourneySync() { |
| 240 | - MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi(); | ||
| 241 | // 1.1 获取 Midjourney 平台,状态在 “进行中” 的 image | 257 | // 1.1 获取 Midjourney 平台,状态在 “进行中” 的 image |
| 242 | - List<AiImageDO> imageList = imageMapper.selectListByStatusAndPlatform( | 258 | + List<AiImageDO> images = imageMapper.selectListByStatusAndPlatform( |
| 243 | AiImageStatusEnum.IN_PROGRESS.getStatus(), AiPlatformEnum.MIDJOURNEY.getPlatform()); | 259 | AiImageStatusEnum.IN_PROGRESS.getStatus(), AiPlatformEnum.MIDJOURNEY.getPlatform()); |
| 244 | - if (CollUtil.isEmpty(imageList)) { | 260 | + if (CollUtil.isEmpty(images)) { |
| 245 | return 0; | 261 | return 0; |
| 246 | } | 262 | } |
| 247 | // 1.2 调用 Midjourney Proxy 获取任务进展 | 263 | // 1.2 调用 Midjourney Proxy 获取任务进展 |
| 248 | - List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(convertSet(imageList, AiImageDO::getTaskId)); | 264 | + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(images.get(0).getModelId()); |
| 265 | + List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(convertSet(images, AiImageDO::getTaskId)); | ||
| 249 | Map<String, MidjourneyApi.Notify> taskMap = convertMap(taskList, MidjourneyApi.Notify::id); | 266 | Map<String, MidjourneyApi.Notify> taskMap = convertMap(taskList, MidjourneyApi.Notify::id); |
| 250 | 267 | ||
| 251 | // 2. 逐个处理,更新进展 | 268 | // 2. 逐个处理,更新进展 |
| 252 | int count = 0; | 269 | int count = 0; |
| 253 | - for (AiImageDO image : imageList) { | 270 | + for (AiImageDO image : images) { |
| 254 | MidjourneyApi.Notify notify = taskMap.get(image.getTaskId()); | 271 | MidjourneyApi.Notify notify = taskMap.get(image.getTaskId()); |
| 255 | if (notify == null) { | 272 | if (notify == null) { |
| 256 | log.error("[midjourneySync][image({}) 查询不到进展]", image); | 273 | log.error("[midjourneySync][image({}) 查询不到进展]", image); |
| @@ -308,12 +325,12 @@ public class AiImageServiceImpl implements AiImageService { | @@ -308,12 +325,12 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 308 | 325 | ||
| 309 | @Override | 326 | @Override |
| 310 | public Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO) { | 327 | public Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO) { |
| 311 | - MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi(); | ||
| 312 | // 1.1 检查 image | 328 | // 1.1 检查 image |
| 313 | AiImageDO image = validateImageExists(reqVO.getId()); | 329 | AiImageDO image = validateImageExists(reqVO.getId()); |
| 314 | if (ObjUtil.notEqual(userId, image.getUserId())) { | 330 | if (ObjUtil.notEqual(userId, image.getUserId())) { |
| 315 | throw exception(IMAGE_NOT_EXISTS); | 331 | throw exception(IMAGE_NOT_EXISTS); |
| 316 | } | 332 | } |
| 333 | + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(image.getModelId()); | ||
| 317 | // 1.2 检查 customId | 334 | // 1.2 检查 customId |
| 318 | MidjourneyApi.Button button = CollUtil.findOne(image.getButtons(), | 335 | MidjourneyApi.Button button = CollUtil.findOne(image.getButtons(), |
| 319 | buttonX -> buttonX.customId().equals(reqVO.getCustomId())); | 336 | buttonX -> buttonX.customId().equals(reqVO.getCustomId())); |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java
| 1 | package cn.iocoder.yudao.module.ai.service.knowledge; | 1 | package cn.iocoder.yudao.module.ai.service.knowledge; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO; | ||
| 4 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; | 5 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; |
| 5 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; | 6 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; |
| 7 | +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO; | ||
| 6 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; | 8 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; |
| 7 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; | 9 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; |
| 8 | 10 | ||
| 11 | +import java.util.Collection; | ||
| 12 | +import java.util.List; | ||
| 13 | +import java.util.Map; | ||
| 14 | + | ||
| 15 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; | ||
| 16 | + | ||
| 9 | /** | 17 | /** |
| 10 | - * AI 知识库-文档 Service 接口 | 18 | + * AI 知识库文档 Service 接口 |
| 11 | * | 19 | * |
| 12 | * @author xiaoxin | 20 | * @author xiaoxin |
| 13 | */ | 21 | */ |
| 14 | public interface AiKnowledgeDocumentService { | 22 | public interface AiKnowledgeDocumentService { |
| 15 | 23 | ||
| 16 | /** | 24 | /** |
| 17 | - * 创建文档 | 25 | + * 创建文档(单个) |
| 18 | * | 26 | * |
| 19 | * @param createReqVO 文档创建 Request VO | 27 | * @param createReqVO 文档创建 Request VO |
| 20 | * @return 文档编号 | 28 | * @return 文档编号 |
| 21 | */ | 29 | */ |
| 22 | Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO); | 30 | Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO); |
| 23 | 31 | ||
| 32 | + /** | ||
| 33 | + * 创建文档(多个) | ||
| 34 | + * | ||
| 35 | + * @param createListReqVO 批量创建 Request VO | ||
| 36 | + * @return 文档编号列表 | ||
| 37 | + */ | ||
| 38 | + List<Long> createKnowledgeDocumentList(AiKnowledgeDocumentCreateListReqVO createListReqVO); | ||
| 24 | 39 | ||
| 25 | /** | 40 | /** |
| 26 | * 获取文档分页 | 41 | * 获取文档分页 |
| @@ -31,9 +46,73 @@ public interface AiKnowledgeDocumentService { | @@ -31,9 +46,73 @@ public interface AiKnowledgeDocumentService { | ||
| 31 | PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO); | 46 | PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO); |
| 32 | 47 | ||
| 33 | /** | 48 | /** |
| 49 | + * 获取文档详情 | ||
| 50 | + * | ||
| 51 | + * @param id 文档编号 | ||
| 52 | + * @return 文档详情 | ||
| 53 | + */ | ||
| 54 | + AiKnowledgeDocumentDO getKnowledgeDocument(Long id); | ||
| 55 | + | ||
| 56 | + /** | ||
| 34 | * 更新文档 | 57 | * 更新文档 |
| 35 | * | 58 | * |
| 36 | * @param reqVO 更新信息 | 59 | * @param reqVO 更新信息 |
| 37 | */ | 60 | */ |
| 38 | void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO); | 61 | void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO); |
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 更新文档状态 | ||
| 65 | + * | ||
| 66 | + * @param reqVO 更新状态信息 | ||
| 67 | + */ | ||
| 68 | + void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO); | ||
| 69 | + | ||
| 70 | + /** | ||
| 71 | + * 更新文档检索次数(增加 +1) | ||
| 72 | + * | ||
| 73 | + * @param ids 文档编号列表 | ||
| 74 | + */ | ||
| 75 | + void updateKnowledgeDocumentRetrievalCountIncr(Collection<Long> ids); | ||
| 76 | + | ||
| 77 | + /** | ||
| 78 | + * 删除文档 | ||
| 79 | + * | ||
| 80 | + * @param id 文档编号 | ||
| 81 | + */ | ||
| 82 | + void deleteKnowledgeDocument(Long id); | ||
| 83 | + | ||
| 84 | + /** | ||
| 85 | + * 校验文档是否存在 | ||
| 86 | + * | ||
| 87 | + * @param id 文档编号 | ||
| 88 | + * @return 文档信息 | ||
| 89 | + */ | ||
| 90 | + AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id); | ||
| 91 | + | ||
| 92 | + /** | ||
| 93 | + * 读取 URL 内容 | ||
| 94 | + * | ||
| 95 | + * @param url URL | ||
| 96 | + * @return 内容 | ||
| 97 | + */ | ||
| 98 | + String readUrl(String url); | ||
| 99 | + | ||
| 100 | + /** | ||
| 101 | + * 获取文档列表 | ||
| 102 | + * | ||
| 103 | + * @param ids 文档编号列表 | ||
| 104 | + * @return 文档列表 | ||
| 105 | + */ | ||
| 106 | + List<AiKnowledgeDocumentDO> getKnowledgeDocumentList(Collection<Long> ids); | ||
| 107 | + | ||
| 108 | + /** | ||
| 109 | + * 获取文档 Map | ||
| 110 | + * | ||
| 111 | + * @param ids 文档编号列表 | ||
| 112 | + * @return 文档 Map | ||
| 113 | + */ | ||
| 114 | + default Map<Long, AiKnowledgeDocumentDO> getKnowledgeDocumentMap(Collection<Long> ids) { | ||
| 115 | + return convertMap(getKnowledgeDocumentList(ids), AiKnowledgeDocumentDO::getId); | ||
| 116 | + } | ||
| 117 | + | ||
| 39 | } | 118 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
| 1 | package cn.iocoder.yudao.module.ai.service.knowledge; | 1 | package cn.iocoder.yudao.module.ai.service.knowledge; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.util.ObjUtil; | ||
| 5 | +import cn.hutool.core.util.StrUtil; | ||
| 4 | import cn.hutool.http.HttpUtil; | 6 | import cn.hutool.http.HttpUtil; |
| 5 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 7 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 8 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||
| 8 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 9 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 10 | +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO; | ||
| 9 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; | 11 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; |
| 10 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; | 12 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; |
| 13 | +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO; | ||
| 11 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; | 14 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; |
| 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; | 15 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; |
| 13 | -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; | ||
| 14 | import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper; | 16 | import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper; |
| 15 | -import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper; | ||
| 16 | -import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum; | ||
| 17 | import jakarta.annotation.Resource; | 17 | import jakarta.annotation.Resource; |
| 18 | import lombok.extern.slf4j.Slf4j; | 18 | import lombok.extern.slf4j.Slf4j; |
| 19 | import org.springframework.ai.document.Document; | 19 | import org.springframework.ai.document.Document; |
| 20 | import org.springframework.ai.reader.tika.TikaDocumentReader; | 20 | import org.springframework.ai.reader.tika.TikaDocumentReader; |
| 21 | import org.springframework.ai.tokenizer.TokenCountEstimator; | 21 | import org.springframework.ai.tokenizer.TokenCountEstimator; |
| 22 | -import org.springframework.ai.transformer.splitter.TokenTextSplitter; | ||
| 23 | -import org.springframework.ai.vectorstore.VectorStore; | 22 | +import org.springframework.context.annotation.Lazy; |
| 24 | import org.springframework.core.io.ByteArrayResource; | 23 | import org.springframework.core.io.ByteArrayResource; |
| 25 | import org.springframework.stereotype.Service; | 24 | import org.springframework.stereotype.Service; |
| 26 | import org.springframework.transaction.annotation.Transactional; | 25 | import org.springframework.transaction.annotation.Transactional; |
| 27 | 26 | ||
| 27 | +import java.util.ArrayList; | ||
| 28 | +import java.util.Collection; | ||
| 29 | +import java.util.Collections; | ||
| 28 | import java.util.List; | 30 | import java.util.List; |
| 29 | 31 | ||
| 30 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 32 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 31 | -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_DOCUMENT_NOT_EXISTS; | 33 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| 34 | +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; | ||
| 32 | 35 | ||
| 33 | /** | 36 | /** |
| 34 | * AI 知识库文档 Service 实现类 | 37 | * AI 知识库文档 Service 实现类 |
| @@ -40,91 +43,172 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_DOCU | @@ -40,91 +43,172 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_DOCU | ||
| 40 | public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentService { | 43 | public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentService { |
| 41 | 44 | ||
| 42 | @Resource | 45 | @Resource |
| 43 | - private AiKnowledgeDocumentMapper documentMapper; | ||
| 44 | - @Resource | ||
| 45 | - private AiKnowledgeSegmentMapper segmentMapper; | 46 | + private AiKnowledgeDocumentMapper knowledgeDocumentMapper; |
| 46 | 47 | ||
| 47 | @Resource | 48 | @Resource |
| 48 | private TokenCountEstimator tokenCountEstimator; | 49 | private TokenCountEstimator tokenCountEstimator; |
| 50 | + | ||
| 51 | + @Resource | ||
| 52 | + private AiKnowledgeSegmentService knowledgeSegmentService; | ||
| 49 | @Resource | 53 | @Resource |
| 54 | + @Lazy // 延迟加载,避免循环依赖 | ||
| 50 | private AiKnowledgeService knowledgeService; | 55 | private AiKnowledgeService knowledgeService; |
| 51 | 56 | ||
| 52 | @Override | 57 | @Override |
| 53 | - @Transactional(rollbackFor = Exception.class) | ||
| 54 | public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) { | 58 | public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) { |
| 55 | - // 0. 校验并获取向量存储实例 | ||
| 56 | - VectorStore vectorStore = knowledgeService.getVectorStoreById(createReqVO.getKnowledgeId()); | 59 | + // 1. 校验参数 |
| 60 | + knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId()); | ||
| 57 | 61 | ||
| 58 | - // 1.1 下载文档 | ||
| 59 | - TikaDocumentReader loader = new TikaDocumentReader(downloadFile(createReqVO.getUrl())); | ||
| 60 | - List<Document> documents = loader.get(); | ||
| 61 | - Document document = CollUtil.getFirst(documents); | ||
| 62 | - // 1.2 文档记录入库 | ||
| 63 | - String content = document.getContent(); | 62 | + // 2. 下载文档 |
| 63 | + String content = readUrl(createReqVO.getUrl()); | ||
| 64 | + | ||
| 65 | + // 3. 文档记录入库 | ||
| 64 | AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class) | 66 | AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class) |
| 65 | - .setTokens(tokenCountEstimator.estimate(content)).setWordCount(content.length()) | ||
| 66 | - .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus()); | ||
| 67 | - documentMapper.insert(documentDO); | ||
| 68 | - Long documentId = documentDO.getId(); | ||
| 69 | - if (CollUtil.isEmpty(documents)) { | ||
| 70 | - return documentId; | 67 | + .setContent(content).setContentLength(content.length()).setTokens(tokenCountEstimator.estimate(content)) |
| 68 | + .setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||
| 69 | + knowledgeDocumentMapper.insert(documentDO); | ||
| 70 | + | ||
| 71 | + // 4. 文档切片入库(异步) | ||
| 72 | + knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(documentDO.getId(), content); | ||
| 73 | + return documentDO.getId(); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + @Override | ||
| 77 | + public List<Long> createKnowledgeDocumentList(AiKnowledgeDocumentCreateListReqVO createListReqVO) { | ||
| 78 | + // 1. 校验参数 | ||
| 79 | + knowledgeService.validateKnowledgeExists(createListReqVO.getKnowledgeId()); | ||
| 80 | + | ||
| 81 | + // 2. 下载文档 | ||
| 82 | + List<String> contents = convertList(createListReqVO.getList(), document -> readUrl(document.getUrl())); | ||
| 83 | + | ||
| 84 | + // 3. 文档记录入库 | ||
| 85 | + List<AiKnowledgeDocumentDO> documentDOs = new ArrayList<>(createListReqVO.getList().size()); | ||
| 86 | + for (int i = 0; i < createListReqVO.getList().size(); i++) { | ||
| 87 | + AiKnowledgeDocumentCreateListReqVO.Document documentVO = createListReqVO.getList().get(i); | ||
| 88 | + String content = contents.get(i); | ||
| 89 | + documentDOs.add(BeanUtils.toBean(documentVO, AiKnowledgeDocumentDO.class) | ||
| 90 | + .setKnowledgeId(createListReqVO.getKnowledgeId()) | ||
| 91 | + .setContent(content).setContentLength(content.length()) | ||
| 92 | + .setTokens(tokenCountEstimator.estimate(content)) | ||
| 93 | + .setSegmentMaxTokens(createListReqVO.getSegmentMaxTokens()) | ||
| 94 | + .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||
| 71 | } | 95 | } |
| 96 | + knowledgeDocumentMapper.insertBatch(documentDOs); | ||
| 72 | 97 | ||
| 73 | - // 2 构造文本分段器 | ||
| 74 | - TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(createReqVO.getDefaultSegmentTokens(), createReqVO.getMinSegmentWordCount(), createReqVO.getMinChunkLengthToEmbed(), | ||
| 75 | - createReqVO.getMaxNumSegments(), createReqVO.getKeepSeparator()); | ||
| 76 | - // 2.1 文档分段 | ||
| 77 | - List<Document> segments = tokenTextSplitter.apply(documents); | ||
| 78 | - // 2.2 分段内容入库 | ||
| 79 | - List<AiKnowledgeSegmentDO> segmentDOList = CollectionUtils.convertList(segments, | ||
| 80 | - segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId) | ||
| 81 | - .setKnowledgeId(createReqVO.getKnowledgeId()).setVectorId(segment.getId()) | ||
| 82 | - .setTokens(tokenCountEstimator.estimate(segment.getContent())).setWordCount(segment.getContent().length()) | ||
| 83 | - .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||
| 84 | - segmentMapper.insertBatch(segmentDOList); | ||
| 85 | - | ||
| 86 | - // 3. 向量化并存储 | ||
| 87 | - segments.forEach(segment -> segment.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, createReqVO.getKnowledgeId())); | ||
| 88 | - vectorStore.add(segments); | ||
| 89 | - return documentId; | 98 | + // 4. 批量创建文档切片(异步) |
| 99 | + documentDOs.forEach(documentDO -> knowledgeSegmentService | ||
| 100 | + .createKnowledgeSegmentBySplitContentAsync(documentDO.getId(), documentDO.getContent())); | ||
| 101 | + return convertList(documentDOs, AiKnowledgeDocumentDO::getId); | ||
| 90 | } | 102 | } |
| 91 | 103 | ||
| 92 | @Override | 104 | @Override |
| 93 | public PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO) { | 105 | public PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO) { |
| 94 | - return documentMapper.selectPage(pageReqVO); | 106 | + return knowledgeDocumentMapper.selectPage(pageReqVO); |
| 107 | + } | ||
| 108 | + | ||
| 109 | + @Override | ||
| 110 | + public AiKnowledgeDocumentDO getKnowledgeDocument(Long id) { | ||
| 111 | + return knowledgeDocumentMapper.selectById(id); | ||
| 95 | } | 112 | } |
| 96 | 113 | ||
| 97 | @Override | 114 | @Override |
| 98 | public void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO) { | 115 | public void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO) { |
| 99 | // 1. 校验文档是否存在 | 116 | // 1. 校验文档是否存在 |
| 100 | - validateKnowledgeDocumentExists(reqVO.getId()); | 117 | + AiKnowledgeDocumentDO oldDocument = validateKnowledgeDocumentExists(reqVO.getId()); |
| 118 | + | ||
| 101 | // 2. 更新文档 | 119 | // 2. 更新文档 |
| 102 | AiKnowledgeDocumentDO document = BeanUtils.toBean(reqVO, AiKnowledgeDocumentDO.class); | 120 | AiKnowledgeDocumentDO document = BeanUtils.toBean(reqVO, AiKnowledgeDocumentDO.class); |
| 103 | - documentMapper.updateById(document); | 121 | + knowledgeDocumentMapper.updateById(document); |
| 122 | + | ||
| 123 | + // 3. 如果处于开启状态,并且最大 tokens 发生变化,则 segment 需要重新索引 | ||
| 124 | + if (CommonStatusEnum.isEnable(oldDocument.getStatus()) | ||
| 125 | + && reqVO.getSegmentMaxTokens() != null | ||
| 126 | + && ObjUtil.notEqual(reqVO.getSegmentMaxTokens(), oldDocument.getSegmentMaxTokens())) { | ||
| 127 | + // 删除旧的文档切片 | ||
| 128 | + knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(reqVO.getId()); | ||
| 129 | + // 重新创建文档切片 | ||
| 130 | + knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(reqVO.getId(), oldDocument.getContent()); | ||
| 131 | + } | ||
| 104 | } | 132 | } |
| 105 | 133 | ||
| 106 | - /** | ||
| 107 | - * 校验文档是否存在 | ||
| 108 | - * | ||
| 109 | - * @param id 文档编号 | ||
| 110 | - * @return 文档信息 | ||
| 111 | - */ | ||
| 112 | - private AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id) { | ||
| 113 | - AiKnowledgeDocumentDO knowledgeDocument = documentMapper.selectById(id); | 134 | + @Override |
| 135 | + public void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO) { | ||
| 136 | + // 1. 校验存在 | ||
| 137 | + AiKnowledgeDocumentDO document = validateKnowledgeDocumentExists(reqVO.getId()); | ||
| 138 | + | ||
| 139 | + // 2. 更新状态 | ||
| 140 | + knowledgeDocumentMapper.updateById(new AiKnowledgeDocumentDO() | ||
| 141 | + .setId(reqVO.getId()).setStatus(reqVO.getStatus())); | ||
| 142 | + | ||
| 143 | + // 3. 处理文档切片 | ||
| 144 | + if (CommonStatusEnum.isEnable(reqVO.getStatus())) { | ||
| 145 | + knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(reqVO.getId(), document.getContent()); | ||
| 146 | + } else { | ||
| 147 | + knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(reqVO.getId()); | ||
| 148 | + } | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + @Override | ||
| 152 | + @Transactional(rollbackFor = Exception.class) | ||
| 153 | + public void deleteKnowledgeDocument(Long id) { | ||
| 154 | + // 1. 校验存在 | ||
| 155 | + validateKnowledgeDocumentExists(id); | ||
| 156 | + | ||
| 157 | + // 2. 删除 | ||
| 158 | + knowledgeDocumentMapper.deleteById(id); | ||
| 159 | + | ||
| 160 | + // 3. 删除对应的段落 | ||
| 161 | + knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(id); | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + @Override | ||
| 165 | + public void updateKnowledgeDocumentRetrievalCountIncr(Collection<Long> ids) { | ||
| 166 | + if (CollUtil.isEmpty(ids)) { | ||
| 167 | + return; | ||
| 168 | + } | ||
| 169 | + knowledgeDocumentMapper.updateRetrievalCountIncr(ids); | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + @Override | ||
| 173 | + public AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id) { | ||
| 174 | + AiKnowledgeDocumentDO knowledgeDocument = knowledgeDocumentMapper.selectById(id); | ||
| 114 | if (knowledgeDocument == null) { | 175 | if (knowledgeDocument == null) { |
| 115 | throw exception(KNOWLEDGE_DOCUMENT_NOT_EXISTS); | 176 | throw exception(KNOWLEDGE_DOCUMENT_NOT_EXISTS); |
| 116 | } | 177 | } |
| 117 | return knowledgeDocument; | 178 | return knowledgeDocument; |
| 118 | } | 179 | } |
| 119 | 180 | ||
| 120 | - private org.springframework.core.io.Resource downloadFile(String url) { | 181 | + @Override |
| 182 | + public String readUrl(String url) { | ||
| 183 | + // 下载文件 | ||
| 184 | + ByteArrayResource resource; | ||
| 121 | try { | 185 | try { |
| 122 | byte[] bytes = HttpUtil.downloadBytes(url); | 186 | byte[] bytes = HttpUtil.downloadBytes(url); |
| 123 | - return new ByteArrayResource(bytes); | 187 | + if (bytes.length == 0) { |
| 188 | + throw exception(KNOWLEDGE_DOCUMENT_FILE_EMPTY); | ||
| 189 | + } | ||
| 190 | + resource = new ByteArrayResource(bytes); | ||
| 124 | } catch (Exception e) { | 191 | } catch (Exception e) { |
| 125 | - log.error("[downloadFile][url({}) 下载失败]", url, e); | ||
| 126 | - throw new RuntimeException(e); | 192 | + log.error("[readUrl][url({}) 读取失败]", url, e); |
| 193 | + throw exception(KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL); | ||
| 194 | + } | ||
| 195 | + | ||
| 196 | + // 读取文件 | ||
| 197 | + TikaDocumentReader loader = new TikaDocumentReader(resource); | ||
| 198 | + List<Document> documents = loader.get(); | ||
| 199 | + Document document = CollUtil.getFirst(documents); | ||
| 200 | + if (document == null || StrUtil.isEmpty(document.getText())) { | ||
| 201 | + throw exception(KNOWLEDGE_DOCUMENT_FILE_READ_FAIL); | ||
| 202 | + } | ||
| 203 | + return document.getText(); | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + @Override | ||
| 207 | + public List<AiKnowledgeDocumentDO> getKnowledgeDocumentList(Collection<Long> ids) { | ||
| 208 | + if (CollUtil.isEmpty(ids)) { | ||
| 209 | + return Collections.emptyList(); | ||
| 127 | } | 210 | } |
| 211 | + return knowledgeDocumentMapper.selectBatchIds(ids); | ||
| 128 | } | 212 | } |
| 129 | 213 | ||
| 130 | } | 214 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
| @@ -2,12 +2,19 @@ package cn.iocoder.yudao.module.ai.service.knowledge; | @@ -2,12 +2,19 @@ package cn.iocoder.yudao.module.ai.service.knowledge; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; | 4 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; |
| 5 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO; | ||
| 6 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; | 5 | +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO; |
| 6 | +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSaveReqVO; | ||
| 7 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; | 7 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; |
| 8 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; | 8 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; |
| 9 | +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO; | ||
| 10 | +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO; | ||
| 11 | +import org.springframework.scheduling.annotation.Async; | ||
| 9 | 12 | ||
| 13 | +import java.util.Collection; | ||
| 10 | import java.util.List; | 14 | import java.util.List; |
| 15 | +import java.util.Map; | ||
| 16 | + | ||
| 17 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; | ||
| 11 | 18 | ||
| 12 | /** | 19 | /** |
| 13 | * AI 知识库段落 Service 接口 | 20 | * AI 知识库段落 Service 接口 |
| @@ -17,6 +24,32 @@ import java.util.List; | @@ -17,6 +24,32 @@ import java.util.List; | ||
| 17 | public interface AiKnowledgeSegmentService { | 24 | public interface AiKnowledgeSegmentService { |
| 18 | 25 | ||
| 19 | /** | 26 | /** |
| 27 | + * 获取知识库段落详情 | ||
| 28 | + * | ||
| 29 | + * @param id 段落编号 | ||
| 30 | + * @return 段落详情 | ||
| 31 | + */ | ||
| 32 | + AiKnowledgeSegmentDO getKnowledgeSegment(Long id); | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 获取知识库段落列表 | ||
| 36 | + * | ||
| 37 | + * @param ids 段落编号列表 | ||
| 38 | + * @return 段落列表 | ||
| 39 | + */ | ||
| 40 | + List<AiKnowledgeSegmentDO> getKnowledgeSegmentList(Collection<Long> ids); | ||
| 41 | + | ||
| 42 | + /** | ||
| 43 | + * 获取知识库段落 Map | ||
| 44 | + * | ||
| 45 | + * @param ids 段落编号列表 | ||
| 46 | + * @return 段落 Map | ||
| 47 | + */ | ||
| 48 | + default Map<Long, AiKnowledgeSegmentDO> getKnowledgeSegmentMap(Collection<Long> ids) { | ||
| 49 | + return convertMap(getKnowledgeSegmentList(ids), AiKnowledgeSegmentDO::getId); | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + /** | ||
| 20 | * 获取段落分页 | 53 | * 获取段落分页 |
| 21 | * | 54 | * |
| 22 | * @param pageReqVO 分页查询 | 55 | * @param pageReqVO 分页查询 |
| @@ -25,11 +58,38 @@ public interface AiKnowledgeSegmentService { | @@ -25,11 +58,38 @@ public interface AiKnowledgeSegmentService { | ||
| 25 | PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO); | 58 | PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO); |
| 26 | 59 | ||
| 27 | /** | 60 | /** |
| 61 | + * 基于 content 内容,切片创建多个段落 | ||
| 62 | + * | ||
| 63 | + * @param documentId 知识库文档编号 | ||
| 64 | + * @param content 文档内容 | ||
| 65 | + */ | ||
| 66 | + void createKnowledgeSegmentBySplitContent(Long documentId, String content); | ||
| 67 | + | ||
| 68 | + /** | ||
| 69 | + * 【异步】基于 content 内容,切片创建多个段落 | ||
| 70 | + * | ||
| 71 | + * @param documentId 知识库文档编号 | ||
| 72 | + * @param content 文档内容 | ||
| 73 | + */ | ||
| 74 | + @Async | ||
| 75 | + default void createKnowledgeSegmentBySplitContentAsync(Long documentId, String content) { | ||
| 76 | + createKnowledgeSegmentBySplitContent(documentId, content); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + /** | ||
| 80 | + * 创建知识库段落 | ||
| 81 | + * | ||
| 82 | + * @param createReqVO 创建信息 | ||
| 83 | + * @return 段落编号 | ||
| 84 | + */ | ||
| 85 | + Long createKnowledgeSegment(AiKnowledgeSegmentSaveReqVO createReqVO); | ||
| 86 | + | ||
| 87 | + /** | ||
| 28 | * 更新段落的内容 | 88 | * 更新段落的内容 |
| 29 | * | 89 | * |
| 30 | * @param reqVO 更新内容 | 90 | * @param reqVO 更新内容 |
| 31 | */ | 91 | */ |
| 32 | - void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO); | 92 | + void updateKnowledgeSegment(AiKnowledgeSegmentSaveReqVO reqVO); |
| 33 | 93 | ||
| 34 | /** | 94 | /** |
| 35 | * 更新段落的状态 | 95 | * 更新段落的状态 |
| @@ -39,11 +99,52 @@ public interface AiKnowledgeSegmentService { | @@ -39,11 +99,52 @@ public interface AiKnowledgeSegmentService { | ||
| 39 | void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO); | 99 | void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO); |
| 40 | 100 | ||
| 41 | /** | 101 | /** |
| 42 | - * 召回段落 | 102 | + * 重新索引知识库下的所有文档段落 |
| 103 | + * | ||
| 104 | + * @param knowledgeId 知识库编号 | ||
| 105 | + */ | ||
| 106 | + void reindexKnowledgeSegmentByKnowledgeId(Long knowledgeId); | ||
| 107 | + | ||
| 108 | + /** | ||
| 109 | + * 【异步】重新索引知识库下的所有文档段落 | ||
| 110 | + * | ||
| 111 | + * @param knowledgeId 知识库编号 | ||
| 112 | + */ | ||
| 113 | + @Async | ||
| 114 | + default void reindexByKnowledgeIdAsync(Long knowledgeId) { | ||
| 115 | + reindexKnowledgeSegmentByKnowledgeId(knowledgeId); | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + /** | ||
| 119 | + * 根据文档编号删除段落 | ||
| 120 | + * | ||
| 121 | + * @param documentId 文档编号 | ||
| 122 | + */ | ||
| 123 | + void deleteKnowledgeSegmentByDocumentId(Long documentId); | ||
| 124 | + | ||
| 125 | + /** | ||
| 126 | + * 搜索知识库段落,并返回结果 | ||
| 127 | + * | ||
| 128 | + * @param reqBO 搜索请求信息 | ||
| 129 | + * @return 搜索结果段落列表 | ||
| 130 | + */ | ||
| 131 | + List<AiKnowledgeSegmentSearchRespBO> searchKnowledgeSegment(AiKnowledgeSegmentSearchReqBO reqBO); | ||
| 132 | + | ||
| 133 | + /** | ||
| 134 | + * 根据 URL 内容,切片创建多个段落 | ||
| 135 | + * | ||
| 136 | + * @param url URL 地址 | ||
| 137 | + * @param segmentMaxTokens 段落最大 Token 数 | ||
| 138 | + * @return 切片后的段落列表 | ||
| 139 | + */ | ||
| 140 | + List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens); | ||
| 141 | + | ||
| 142 | + /** | ||
| 143 | + * 获取文档处理进度(多个) | ||
| 43 | * | 144 | * |
| 44 | - * @param reqVO 召回请求信息 | ||
| 45 | - * @return 召回的段落 | 145 | + * @param documentIds 文档编号列表 |
| 146 | + * @return 文档处理列表 | ||
| 46 | */ | 147 | */ |
| 47 | - List<AiKnowledgeSegmentDO> similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO); | 148 | + List<AiKnowledgeSegmentProcessRespVO> getKnowledgeSegmentProcessList(List<Long> documentIds); |
| 48 | 149 | ||
| 49 | } | 150 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
| @@ -2,31 +2,39 @@ package cn.iocoder.yudao.module.ai.service.knowledge; | @@ -2,31 +2,39 @@ package cn.iocoder.yudao.module.ai.service.knowledge; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.collection.ListUtil; | 4 | import cn.hutool.core.collection.ListUtil; |
| 5 | +import cn.hutool.core.util.ObjUtil; | ||
| 6 | +import cn.hutool.core.util.StrUtil; | ||
| 5 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 7 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 8 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 9 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 8 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; | 10 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; |
| 9 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO; | ||
| 10 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; | 11 | +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO; |
| 12 | +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSaveReqVO; | ||
| 11 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; | 13 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; |
| 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; | 14 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; |
| 15 | +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; | ||
| 13 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; | 16 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; |
| 14 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | ||
| 15 | import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper; | 17 | import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper; |
| 16 | -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; | ||
| 17 | -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; | 18 | +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO; |
| 19 | +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO; | ||
| 20 | +import cn.iocoder.yudao.module.ai.service.model.AiModelService; | ||
| 18 | import jakarta.annotation.Resource; | 21 | import jakarta.annotation.Resource; |
| 19 | import lombok.extern.slf4j.Slf4j; | 22 | import lombok.extern.slf4j.Slf4j; |
| 20 | import org.springframework.ai.document.Document; | 23 | import org.springframework.ai.document.Document; |
| 24 | +import org.springframework.ai.tokenizer.TokenCountEstimator; | ||
| 25 | +import org.springframework.ai.transformer.splitter.TextSplitter; | ||
| 26 | +import org.springframework.ai.transformer.splitter.TokenTextSplitter; | ||
| 21 | import org.springframework.ai.vectorstore.SearchRequest; | 27 | import org.springframework.ai.vectorstore.SearchRequest; |
| 22 | import org.springframework.ai.vectorstore.VectorStore; | 28 | import org.springframework.ai.vectorstore.VectorStore; |
| 23 | import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder; | 29 | import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder; |
| 30 | +import org.springframework.context.annotation.Lazy; | ||
| 24 | import org.springframework.stereotype.Service; | 31 | import org.springframework.stereotype.Service; |
| 25 | 32 | ||
| 26 | -import java.util.List; | ||
| 27 | -import java.util.Objects; | 33 | +import java.util.*; |
| 28 | 34 | ||
| 29 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 35 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 36 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||
| 37 | +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG; | ||
| 30 | import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS; | 38 | import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS; |
| 31 | 39 | ||
| 32 | /** | 40 | /** |
| @@ -38,15 +46,28 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGM | @@ -38,15 +46,28 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGM | ||
| 38 | @Slf4j | 46 | @Slf4j |
| 39 | public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService { | 47 | public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService { |
| 40 | 48 | ||
| 49 | + private static final String VECTOR_STORE_METADATA_KNOWLEDGE_ID = "knowledgeId"; | ||
| 50 | + private static final String VECTOR_STORE_METADATA_DOCUMENT_ID = "documentId"; | ||
| 51 | + private static final String VECTOR_STORE_METADATA_SEGMENT_ID = "segmentId"; | ||
| 52 | + | ||
| 53 | + private static final Map<String, Class<?>> VECTOR_STORE_METADATA_TYPES = Map.of( | ||
| 54 | + VECTOR_STORE_METADATA_KNOWLEDGE_ID, String.class, | ||
| 55 | + VECTOR_STORE_METADATA_DOCUMENT_ID, String.class, | ||
| 56 | + VECTOR_STORE_METADATA_SEGMENT_ID, String.class); | ||
| 57 | + | ||
| 41 | @Resource | 58 | @Resource |
| 42 | private AiKnowledgeSegmentMapper segmentMapper; | 59 | private AiKnowledgeSegmentMapper segmentMapper; |
| 43 | 60 | ||
| 44 | @Resource | 61 | @Resource |
| 45 | private AiKnowledgeService knowledgeService; | 62 | private AiKnowledgeService knowledgeService; |
| 46 | @Resource | 63 | @Resource |
| 47 | - private AiChatModelService chatModelService; | 64 | + @Lazy // 延迟加载,避免循环依赖 |
| 65 | + private AiKnowledgeDocumentService knowledgeDocumentService; | ||
| 48 | @Resource | 66 | @Resource |
| 49 | - private AiApiKeyService apiKeyService; | 67 | + private AiModelService modelService; |
| 68 | + | ||
| 69 | + @Resource | ||
| 70 | + private TokenCountEstimator tokenCountEstimator; | ||
| 50 | 71 | ||
| 51 | @Override | 72 | @Override |
| 52 | public PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO) { | 73 | public PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO) { |
| @@ -54,67 +75,198 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService | @@ -54,67 +75,198 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService | ||
| 54 | } | 75 | } |
| 55 | 76 | ||
| 56 | @Override | 77 | @Override |
| 57 | - public void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO) { | 78 | + public void createKnowledgeSegmentBySplitContent(Long documentId, String content) { |
| 79 | + // 1. 校验 | ||
| 80 | + AiKnowledgeDocumentDO documentDO = knowledgeDocumentService.validateKnowledgeDocumentExists(documentId); | ||
| 81 | + AiKnowledgeDO knowledgeDO = knowledgeService.validateKnowledgeExists(documentDO.getKnowledgeId()); | ||
| 82 | + VectorStore vectorStore = getVectorStoreById(knowledgeDO); | ||
| 83 | + | ||
| 84 | + // 2. 文档切片 | ||
| 85 | + List<Document> documentSegments = splitContentByToken(content, documentDO.getSegmentMaxTokens()); | ||
| 86 | + | ||
| 87 | + // 3.1 存储切片 | ||
| 88 | + List<AiKnowledgeSegmentDO> segmentDOs = convertList(documentSegments, segment -> { | ||
| 89 | + if (StrUtil.isEmpty(segment.getText())) { | ||
| 90 | + return null; | ||
| 91 | + } | ||
| 92 | + return new AiKnowledgeSegmentDO().setKnowledgeId(documentDO.getKnowledgeId()).setDocumentId(documentId) | ||
| 93 | + .setContent(segment.getText()).setContentLength(segment.getText().length()) | ||
| 94 | + .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY) | ||
| 95 | + .setTokens(tokenCountEstimator.estimate(segment.getText())) | ||
| 96 | + .setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||
| 97 | + }); | ||
| 98 | + segmentMapper.insertBatch(segmentDOs); | ||
| 99 | + // 3.2 切片向量化 | ||
| 100 | + for (int i = 0; i < documentSegments.size(); i++) { | ||
| 101 | + Document segment = documentSegments.get(i); | ||
| 102 | + AiKnowledgeSegmentDO segmentDO = segmentDOs.get(i); | ||
| 103 | + writeVectorStore(vectorStore, segmentDO, segment); | ||
| 104 | + } | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + @Override | ||
| 108 | + public void updateKnowledgeSegment(AiKnowledgeSegmentSaveReqVO reqVO) { | ||
| 58 | // 1. 校验 | 109 | // 1. 校验 |
| 59 | - AiKnowledgeSegmentDO oldKnowledgeSegment = validateKnowledgeSegmentExists(reqVO.getId()); | 110 | + AiKnowledgeSegmentDO oldSegment = validateKnowledgeSegmentExists(reqVO.getId()); |
| 111 | + | ||
| 112 | + // 2. 删除向量 | ||
| 113 | + VectorStore vectorStore = getVectorStoreById(oldSegment.getKnowledgeId()); | ||
| 114 | + deleteVectorStore(vectorStore, oldSegment); | ||
| 115 | + | ||
| 116 | + // 3.1 更新切片 | ||
| 117 | + AiKnowledgeSegmentDO newSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class); | ||
| 118 | + segmentMapper.updateById(newSegment); | ||
| 119 | + // 3.2 重新向量化,必须开启状态 | ||
| 120 | + if (CommonStatusEnum.isEnable(oldSegment.getStatus())) { | ||
| 121 | + newSegment.setKnowledgeId(oldSegment.getKnowledgeId()).setDocumentId(oldSegment.getDocumentId()); | ||
| 122 | + writeVectorStore(vectorStore, newSegment, new Document(newSegment.getContent())); | ||
| 123 | + } | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + @Override | ||
| 127 | + public void deleteKnowledgeSegmentByDocumentId(Long documentId) { | ||
| 128 | + // 1. 查询需要删除的段落 | ||
| 129 | + List<AiKnowledgeSegmentDO> segments = segmentMapper.selectListByDocumentId(documentId); | ||
| 130 | + if (CollUtil.isEmpty(segments)) { | ||
| 131 | + return; | ||
| 132 | + } | ||
| 60 | 133 | ||
| 61 | - // 2.1 获取知识库向量实例 | ||
| 62 | - VectorStore vectorStore = knowledgeService.getVectorStoreById(oldKnowledgeSegment.getKnowledgeId()); | ||
| 63 | - // 2.2 删除原向量 | ||
| 64 | - vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId())); | ||
| 65 | - // 2.3 重新向量化 | ||
| 66 | - Document document = new Document(reqVO.getContent()); | ||
| 67 | - document.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, oldKnowledgeSegment.getKnowledgeId()); | ||
| 68 | - vectorStore.add(List.of(document)); | 134 | + // 2. 批量删除段落记录 |
| 135 | + segmentMapper.deleteByIds(convertList(segments, AiKnowledgeSegmentDO::getId)); | ||
| 69 | 136 | ||
| 70 | - // 3. 更新段落内容 | ||
| 71 | - AiKnowledgeSegmentDO knowledgeSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class); | ||
| 72 | - knowledgeSegment.setVectorId(document.getId()); | ||
| 73 | - segmentMapper.updateById(knowledgeSegment); | 137 | + // 3. 删除向量存储中的段落 |
| 138 | + VectorStore vectorStore = getVectorStoreById(segments.get(0).getKnowledgeId()); | ||
| 139 | + vectorStore.delete(convertList(segments, AiKnowledgeSegmentDO::getVectorId)); | ||
| 74 | } | 140 | } |
| 75 | 141 | ||
| 76 | @Override | 142 | @Override |
| 77 | public void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO) { | 143 | public void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO) { |
| 78 | - // 0 校验 | ||
| 79 | - AiKnowledgeSegmentDO oldKnowledgeSegment = validateKnowledgeSegmentExists(reqVO.getId()); | ||
| 80 | - // 1 获取知识库向量实例 | ||
| 81 | - VectorStore vectorStore = knowledgeService.getVectorStoreById(oldKnowledgeSegment.getKnowledgeId()); | ||
| 82 | - AiKnowledgeSegmentDO knowledgeSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class); | ||
| 83 | - | ||
| 84 | - if (Objects.equals(reqVO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { | ||
| 85 | - // 2.1 启用重新向量化 | ||
| 86 | - Document document = new Document(oldKnowledgeSegment.getContent()); | ||
| 87 | - document.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, oldKnowledgeSegment.getKnowledgeId()); | ||
| 88 | - vectorStore.add(List.of(document)); | ||
| 89 | - knowledgeSegment.setVectorId(document.getId()); | 144 | + // 1. 校验 |
| 145 | + AiKnowledgeSegmentDO segment = validateKnowledgeSegmentExists(reqVO.getId()); | ||
| 146 | + | ||
| 147 | + // 2. 获取知识库向量实例 | ||
| 148 | + VectorStore vectorStore = getVectorStoreById(segment.getKnowledgeId()); | ||
| 149 | + | ||
| 150 | + // 3. 更新状态 | ||
| 151 | + segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(reqVO.getId()).setStatus(reqVO.getStatus())); | ||
| 152 | + | ||
| 153 | + // 4. 更新向量 | ||
| 154 | + if (CommonStatusEnum.isEnable(reqVO.getStatus())) { | ||
| 155 | + writeVectorStore(vectorStore, segment, new Document(segment.getContent())); | ||
| 90 | } else { | 156 | } else { |
| 91 | - // 2.2 禁用删除向量 | ||
| 92 | - vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId())); | ||
| 93 | - knowledgeSegment.setVectorId(""); | 157 | + deleteVectorStore(vectorStore, segment); |
| 158 | + } | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + @Override | ||
| 162 | + public void reindexKnowledgeSegmentByKnowledgeId(Long knowledgeId) { | ||
| 163 | + // 1.1 校验知识库存在 | ||
| 164 | + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(knowledgeId); | ||
| 165 | + // 1.2 获取知识库向量实例 | ||
| 166 | + VectorStore vectorStore = getVectorStoreById(knowledge); | ||
| 167 | + | ||
| 168 | + // 2.1 查询知识库下的所有启用状态的段落 | ||
| 169 | + List<AiKnowledgeSegmentDO> segments = segmentMapper.selectListByKnowledgeIdAndStatus( | ||
| 170 | + knowledgeId, CommonStatusEnum.ENABLE.getStatus()); | ||
| 171 | + if (CollUtil.isEmpty(segments)) { | ||
| 172 | + return; | ||
| 94 | } | 173 | } |
| 95 | - // 3 更新段落状态 | ||
| 96 | - segmentMapper.updateById(knowledgeSegment); | 174 | + // 2.2 遍历所有段落,重新索引 |
| 175 | + for (AiKnowledgeSegmentDO segment : segments) { | ||
| 176 | + // 删除旧的向量 | ||
| 177 | + deleteVectorStore(vectorStore, segment); | ||
| 178 | + // 重新创建向量 | ||
| 179 | + writeVectorStore(vectorStore, segment, new Document(segment.getContent())); | ||
| 180 | + } | ||
| 181 | + log.info("[reindexKnowledgeSegmentByKnowledgeId][知识库({}) 重新索引完成,共处理 {} 个段落]", | ||
| 182 | + knowledgeId, segments.size()); | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + private void writeVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO, Document segment) { | ||
| 186 | + // 1. 向量存储 | ||
| 187 | + // 为什么要 toString 呢?因为部分 VectorStore 实现,不支持 Long 类型,例如说 QdrantVectorStore | ||
| 188 | + segment.getMetadata().put(VECTOR_STORE_METADATA_KNOWLEDGE_ID, segmentDO.getKnowledgeId().toString()); | ||
| 189 | + segment.getMetadata().put(VECTOR_STORE_METADATA_DOCUMENT_ID, segmentDO.getDocumentId().toString()); | ||
| 190 | + segment.getMetadata().put(VECTOR_STORE_METADATA_SEGMENT_ID, segmentDO.getId().toString()); | ||
| 191 | + vectorStore.add(List.of(segment)); | ||
| 192 | + | ||
| 193 | + // 2. 更新向量 ID | ||
| 194 | + segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(segmentDO.getId()).setVectorId(segment.getId())); | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + private void deleteVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO) { | ||
| 198 | + // 1. 更新向量 ID | ||
| 199 | + if (StrUtil.isEmpty(segmentDO.getVectorId())) { | ||
| 200 | + return; | ||
| 201 | + } | ||
| 202 | + segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(segmentDO.getId()) | ||
| 203 | + .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY)); | ||
| 204 | + | ||
| 205 | + // 2. 删除向量 | ||
| 206 | + vectorStore.delete(List.of(segmentDO.getVectorId())); | ||
| 97 | } | 207 | } |
| 98 | 208 | ||
| 99 | @Override | 209 | @Override |
| 100 | - public List<AiKnowledgeSegmentDO> similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO) { | 210 | + public List<AiKnowledgeSegmentSearchRespBO> searchKnowledgeSegment(AiKnowledgeSegmentSearchReqBO reqBO) { |
| 101 | // 1. 校验 | 211 | // 1. 校验 |
| 102 | - AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqVO.getKnowledgeId()); | ||
| 103 | - AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId()); | ||
| 104 | - | ||
| 105 | - // 2. 获取向量存储实例 | ||
| 106 | - VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId()); | ||
| 107 | - | ||
| 108 | - // 3.1 向量检索 | ||
| 109 | - List<Document> documentList = vectorStore.similaritySearch(SearchRequest.query(reqVO.getContent()) | ||
| 110 | - .withTopK(knowledge.getTopK()) | ||
| 111 | - .withSimilarityThreshold(knowledge.getSimilarityThreshold()) | ||
| 112 | - .withFilterExpression(new FilterExpressionBuilder().eq(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, reqVO.getKnowledgeId()).build())); | ||
| 113 | - if (CollUtil.isEmpty(documentList)) { | 212 | + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqBO.getKnowledgeId()); |
| 213 | + | ||
| 214 | + // 2.1 向量检索 | ||
| 215 | + VectorStore vectorStore = getVectorStoreById(knowledge); | ||
| 216 | + List<Document> documents = vectorStore.similaritySearch(SearchRequest.builder() | ||
| 217 | + .query(reqBO.getContent()) | ||
| 218 | + .topK(ObjUtil.defaultIfNull(reqBO.getTopK(), knowledge.getTopK())) | ||
| 219 | + .similarityThreshold( | ||
| 220 | + ObjUtil.defaultIfNull(reqBO.getSimilarityThreshold(), knowledge.getSimilarityThreshold())) | ||
| 221 | + .filterExpression(new FilterExpressionBuilder() | ||
| 222 | + .eq(VECTOR_STORE_METADATA_KNOWLEDGE_ID, reqBO.getKnowledgeId().toString()) | ||
| 223 | + .build()) | ||
| 224 | + .build()); | ||
| 225 | + if (CollUtil.isEmpty(documents)) { | ||
| 226 | + return ListUtil.empty(); | ||
| 227 | + } | ||
| 228 | + // 2.2 段落召回 | ||
| 229 | + List<AiKnowledgeSegmentDO> segments = segmentMapper | ||
| 230 | + .selectListByVectorIds(convertList(documents, Document::getId)); | ||
| 231 | + if (CollUtil.isEmpty(segments)) { | ||
| 114 | return ListUtil.empty(); | 232 | return ListUtil.empty(); |
| 115 | } | 233 | } |
| 116 | - // 3.2 段落召回 | ||
| 117 | - return segmentMapper.selectListByVectorIds(CollUtil.getFieldValues(documentList, "id", String.class)); | 234 | + |
| 235 | + // 3. 增加召回次数 | ||
| 236 | + segmentMapper.updateRetrievalCountIncrByIds(convertList(segments, AiKnowledgeSegmentDO::getId)); | ||
| 237 | + | ||
| 238 | + // 4. 构建结果 | ||
| 239 | + List<AiKnowledgeSegmentSearchRespBO> result = convertList(segments, segment -> { | ||
| 240 | + Document document = CollUtil.findOne(documents, // 找到对应的文档 | ||
| 241 | + doc -> Objects.equals(doc.getId(), segment.getVectorId())); | ||
| 242 | + if (document == null) { | ||
| 243 | + return null; | ||
| 244 | + } | ||
| 245 | + return BeanUtils.toBean(segment, AiKnowledgeSegmentSearchRespBO.class) | ||
| 246 | + .setScore(document.getScore()); | ||
| 247 | + }); | ||
| 248 | + result.sort((o1, o2) -> Double.compare(o2.getScore(), o1.getScore())); // 按照分数降序排序 | ||
| 249 | + return result; | ||
| 250 | + } | ||
| 251 | + | ||
| 252 | + @Override | ||
| 253 | + public List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens) { | ||
| 254 | + // 1. 读取 URL 内容 | ||
| 255 | + String content = knowledgeDocumentService.readUrl(url); | ||
| 256 | + | ||
| 257 | + // 2. 文档切片 | ||
| 258 | + List<Document> documentSegments = splitContentByToken(content, segmentMaxTokens); | ||
| 259 | + | ||
| 260 | + // 3. 转换为段落对象 | ||
| 261 | + return convertList(documentSegments, segment -> { | ||
| 262 | + if (StrUtil.isEmpty(segment.getText())) { | ||
| 263 | + return null; | ||
| 264 | + } | ||
| 265 | + return new AiKnowledgeSegmentDO() | ||
| 266 | + .setContent(segment.getText()) | ||
| 267 | + .setContentLength(segment.getText().length()) | ||
| 268 | + .setTokens(tokenCountEstimator.estimate(segment.getText())); | ||
| 269 | + }); | ||
| 118 | } | 270 | } |
| 119 | 271 | ||
| 120 | /** | 272 | /** |
| @@ -131,4 +283,75 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService | @@ -131,4 +283,75 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService | ||
| 131 | return knowledgeSegment; | 283 | return knowledgeSegment; |
| 132 | } | 284 | } |
| 133 | 285 | ||
| 286 | + private VectorStore getVectorStoreById(AiKnowledgeDO knowledge) { | ||
| 287 | + return modelService.getOrCreateVectorStore(knowledge.getEmbeddingModelId(), VECTOR_STORE_METADATA_TYPES); | ||
| 288 | + } | ||
| 289 | + | ||
| 290 | + private VectorStore getVectorStoreById(Long knowledgeId) { | ||
| 291 | + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(knowledgeId); | ||
| 292 | + return getVectorStoreById(knowledge); | ||
| 293 | + } | ||
| 294 | + | ||
| 295 | + private static List<Document> splitContentByToken(String content, Integer segmentMaxTokens) { | ||
| 296 | + TextSplitter textSplitter = buildTokenTextSplitter(segmentMaxTokens); | ||
| 297 | + return textSplitter.apply(Collections.singletonList(new Document(content))); | ||
| 298 | + } | ||
| 299 | + | ||
| 300 | + private static TextSplitter buildTokenTextSplitter(Integer segmentMaxTokens) { | ||
| 301 | + return TokenTextSplitter.builder() | ||
| 302 | + .withChunkSize(segmentMaxTokens) | ||
| 303 | + .withMinChunkSizeChars(Integer.MAX_VALUE) // 忽略字符的截断 | ||
| 304 | + .withMinChunkLengthToEmbed(1) // 允许的最小有效分段长度 | ||
| 305 | + .withMaxNumChunks(Integer.MAX_VALUE) | ||
| 306 | + .withKeepSeparator(true) // 保留分隔符 | ||
| 307 | + .build(); | ||
| 308 | + } | ||
| 309 | + | ||
| 310 | + @Override | ||
| 311 | + public List<AiKnowledgeSegmentProcessRespVO> getKnowledgeSegmentProcessList(List<Long> documentIds) { | ||
| 312 | + if (CollUtil.isEmpty(documentIds)) { | ||
| 313 | + return Collections.emptyList(); | ||
| 314 | + } | ||
| 315 | + return segmentMapper.selectProcessList(documentIds); | ||
| 316 | + } | ||
| 317 | + | ||
| 318 | + @Override | ||
| 319 | + public Long createKnowledgeSegment(AiKnowledgeSegmentSaveReqVO createReqVO) { | ||
| 320 | + // 1.1 校验文档是否存在 | ||
| 321 | + AiKnowledgeDocumentDO document = knowledgeDocumentService | ||
| 322 | + .validateKnowledgeDocumentExists(createReqVO.getDocumentId()); | ||
| 323 | + // 1.2 获取知识库信息 | ||
| 324 | + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(document.getKnowledgeId()); | ||
| 325 | + // 1.3 校验 token 熟练 | ||
| 326 | + Integer tokens = tokenCountEstimator.estimate(createReqVO.getContent()); | ||
| 327 | + if (tokens > document.getSegmentMaxTokens()) { | ||
| 328 | + throw exception(KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG, tokens, document.getSegmentMaxTokens()); | ||
| 329 | + } | ||
| 330 | + | ||
| 331 | + // 2. 保存段落 | ||
| 332 | + AiKnowledgeSegmentDO segment = BeanUtils.toBean(createReqVO, AiKnowledgeSegmentDO.class) | ||
| 333 | + .setKnowledgeId(knowledge.getId()).setDocumentId(document.getId()) | ||
| 334 | + .setContentLength(createReqVO.getContent().length()).setTokens(tokens) | ||
| 335 | + .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY) | ||
| 336 | + .setRetrievalCount(0).setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||
| 337 | + segmentMapper.insert(segment); | ||
| 338 | + | ||
| 339 | + // 3. 向量化 | ||
| 340 | + writeVectorStore(getVectorStoreById(knowledge), segment, new Document(segment.getContent())); | ||
| 341 | + return segment.getId(); | ||
| 342 | + } | ||
| 343 | + | ||
| 344 | + @Override | ||
| 345 | + public AiKnowledgeSegmentDO getKnowledgeSegment(Long id) { | ||
| 346 | + return segmentMapper.selectById(id); | ||
| 347 | + } | ||
| 348 | + | ||
| 349 | + @Override | ||
| 350 | + public List<AiKnowledgeSegmentDO> getKnowledgeSegmentList(Collection<Long> ids) { | ||
| 351 | + if (CollUtil.isEmpty(ids)) { | ||
| 352 | + return Collections.emptyList(); | ||
| 353 | + } | ||
| 354 | + return segmentMapper.selectBatchIds(ids); | ||
| 355 | + } | ||
| 356 | + | ||
| 134 | } | 357 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
| 1 | package cn.iocoder.yudao.module.ai.service.knowledge; | 1 | package cn.iocoder.yudao.module.ai.service.knowledge; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO; | ||
| 5 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; | 4 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; |
| 6 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO; | 5 | +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO; |
| 7 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; | 6 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; |
| 8 | -import org.springframework.ai.vectorstore.VectorStore; | 7 | + |
| 8 | +import java.util.List; | ||
| 9 | 9 | ||
| 10 | /** | 10 | /** |
| 11 | * AI 知识库-基础信息 Service 接口 | 11 | * AI 知识库-基础信息 Service 接口 |
| @@ -18,18 +18,24 @@ public interface AiKnowledgeService { | @@ -18,18 +18,24 @@ public interface AiKnowledgeService { | ||
| 18 | * 创建知识库 | 18 | * 创建知识库 |
| 19 | * | 19 | * |
| 20 | * @param createReqVO 创建信息 | 20 | * @param createReqVO 创建信息 |
| 21 | - * @param userId 用户编号 | ||
| 22 | * @return 编号 | 21 | * @return 编号 |
| 23 | */ | 22 | */ |
| 24 | - Long createKnowledge(AiKnowledgeCreateReqVO createReqVO, Long userId); | 23 | + Long createKnowledge(AiKnowledgeSaveReqVO createReqVO); |
| 25 | 24 | ||
| 26 | /** | 25 | /** |
| 27 | * 更新知识库 | 26 | * 更新知识库 |
| 28 | * | 27 | * |
| 29 | * @param updateReqVO 更新信息 | 28 | * @param updateReqVO 更新信息 |
| 30 | - * @param userId 用户编号 | ||
| 31 | */ | 29 | */ |
| 32 | - void updateKnowledge(AiKnowledgeUpdateReqVO updateReqVO, Long userId); | 30 | + void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO); |
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 获得知识库 | ||
| 34 | + * | ||
| 35 | + * @param id 编号 | ||
| 36 | + * @return 知识库 | ||
| 37 | + */ | ||
| 38 | + AiKnowledgeDO getKnowledge(Long id); | ||
| 33 | 39 | ||
| 34 | /** | 40 | /** |
| 35 | * 校验知识库是否存在 | 41 | * 校验知识库是否存在 |
| @@ -41,18 +47,17 @@ public interface AiKnowledgeService { | @@ -41,18 +47,17 @@ public interface AiKnowledgeService { | ||
| 41 | /** | 47 | /** |
| 42 | * 获得知识库分页 | 48 | * 获得知识库分页 |
| 43 | * | 49 | * |
| 44 | - * @param userId 用户编号 | ||
| 45 | * @param pageReqVO 分页查询 | 50 | * @param pageReqVO 分页查询 |
| 46 | * @return 知识库分页 | 51 | * @return 知识库分页 |
| 47 | */ | 52 | */ |
| 48 | - PageResult<AiKnowledgeDO> getKnowledgePage(Long userId, AiKnowledgePageReqVO pageReqVO); | 53 | + PageResult<AiKnowledgeDO> getKnowledgePage(AiKnowledgePageReqVO pageReqVO); |
| 49 | 54 | ||
| 50 | /** | 55 | /** |
| 51 | - * 根据知识库编号获取向量存储实例 | 56 | + * 获得指定状态的知识库列表 |
| 52 | * | 57 | * |
| 53 | - * @param id 知识库编号 | ||
| 54 | - * @return 向量存储实例 | 58 | + * @param status 状态 |
| 59 | + * @return 知识库列表 | ||
| 55 | */ | 60 | */ |
| 56 | - VectorStore getVectorStoreById(Long id); | 61 | + List<AiKnowledgeDO> getKnowledgeSimpleListByStatus(Integer status); |
| 57 | 62 | ||
| 58 | } | 63 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
| @@ -4,19 +4,19 @@ import cn.hutool.core.util.ObjUtil; | @@ -4,19 +4,19 @@ import cn.hutool.core.util.ObjUtil; | ||
| 4 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 4 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 6 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 7 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO; | ||
| 8 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; | 7 | import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; |
| 9 | -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO; | 8 | +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO; |
| 10 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; | 9 | import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; |
| 11 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | 10 | +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; |
| 11 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; | ||
| 12 | import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper; | 12 | import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper; |
| 13 | -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; | ||
| 14 | -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; | 13 | +import cn.iocoder.yudao.module.ai.service.model.AiModelService; |
| 15 | import jakarta.annotation.Resource; | 14 | import jakarta.annotation.Resource; |
| 16 | import lombok.extern.slf4j.Slf4j; | 15 | import lombok.extern.slf4j.Slf4j; |
| 17 | -import org.springframework.ai.vectorstore.VectorStore; | ||
| 18 | import org.springframework.stereotype.Service; | 16 | import org.springframework.stereotype.Service; |
| 19 | 17 | ||
| 18 | +import java.util.List; | ||
| 19 | + | ||
| 20 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 20 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 21 | import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_EXISTS; | 21 | import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_EXISTS; |
| 22 | 22 | ||
| @@ -33,36 +33,43 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { | @@ -33,36 +33,43 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { | ||
| 33 | private AiKnowledgeMapper knowledgeMapper; | 33 | private AiKnowledgeMapper knowledgeMapper; |
| 34 | 34 | ||
| 35 | @Resource | 35 | @Resource |
| 36 | - private AiChatModelService chatModelService; | 36 | + private AiModelService modelService; |
| 37 | @Resource | 37 | @Resource |
| 38 | - private AiApiKeyService apiKeyService; | 38 | + private AiKnowledgeSegmentService knowledgeSegmentService; |
| 39 | 39 | ||
| 40 | @Override | 40 | @Override |
| 41 | - public Long createKnowledge(AiKnowledgeCreateReqVO createReqVO, Long userId) { | 41 | + public Long createKnowledge(AiKnowledgeSaveReqVO createReqVO) { |
| 42 | // 1. 校验模型配置 | 42 | // 1. 校验模型配置 |
| 43 | - AiChatModelDO model = chatModelService.validateChatModel(createReqVO.getModelId()); | 43 | + AiModelDO model = modelService.validateModel(createReqVO.getEmbeddingModelId()); |
| 44 | 44 | ||
| 45 | // 2. 插入知识库 | 45 | // 2. 插入知识库 |
| 46 | - AiKnowledgeDO knowledgeBase = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class) | ||
| 47 | - .setModel(model.getModel()).setUserId(userId).setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||
| 48 | - knowledgeMapper.insert(knowledgeBase); | ||
| 49 | - return knowledgeBase.getId(); | 46 | + AiKnowledgeDO knowledge = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class) |
| 47 | + .setEmbeddingModel(model.getModel()); | ||
| 48 | + knowledgeMapper.insert(knowledge); | ||
| 49 | + return knowledge.getId(); | ||
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | @Override | 52 | @Override |
| 53 | - public void updateKnowledge(AiKnowledgeUpdateReqVO updateReqVO, Long userId) { | 53 | + public void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO) { |
| 54 | // 1.1 校验知识库存在 | 54 | // 1.1 校验知识库存在 |
| 55 | - AiKnowledgeDO knowledgeBaseDO = validateKnowledgeExists(updateReqVO.getId()); | ||
| 56 | - if (ObjUtil.notEqual(knowledgeBaseDO.getUserId(), userId)) { | ||
| 57 | - throw exception(KNOWLEDGE_NOT_EXISTS); | ||
| 58 | - } | 55 | + AiKnowledgeDO oldKnowledge = validateKnowledgeExists(updateReqVO.getId()); |
| 59 | // 1.2 校验模型配置 | 56 | // 1.2 校验模型配置 |
| 60 | - AiChatModelDO model = chatModelService.validateChatModel(updateReqVO.getModelId()); | 57 | + AiModelDO model = modelService.validateModel(updateReqVO.getEmbeddingModelId()); |
| 61 | 58 | ||
| 62 | // 2. 更新知识库 | 59 | // 2. 更新知识库 |
| 63 | - AiKnowledgeDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class); | ||
| 64 | - updateDO.setModel(model.getModel()); | ||
| 65 | - knowledgeMapper.updateById(updateDO); | 60 | + AiKnowledgeDO updateObj = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class) |
| 61 | + .setEmbeddingModel(model.getModel()); | ||
| 62 | + knowledgeMapper.updateById(updateObj); | ||
| 63 | + | ||
| 64 | + // 3. 如果模型变化,需要 reindex 所有的文档 | ||
| 65 | + if (ObjUtil.notEqual(oldKnowledge.getEmbeddingModelId(), updateReqVO.getEmbeddingModelId())) { | ||
| 66 | + knowledgeSegmentService.reindexByKnowledgeIdAsync(updateReqVO.getId()); | ||
| 67 | + } | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + @Override | ||
| 71 | + public AiKnowledgeDO getKnowledge(Long id) { | ||
| 72 | + return knowledgeMapper.selectById(id); | ||
| 66 | } | 73 | } |
| 67 | 74 | ||
| 68 | @Override | 75 | @Override |
| @@ -75,16 +82,13 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { | @@ -75,16 +82,13 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { | ||
| 75 | } | 82 | } |
| 76 | 83 | ||
| 77 | @Override | 84 | @Override |
| 78 | - public PageResult<AiKnowledgeDO> getKnowledgePage(Long userId, AiKnowledgePageReqVO pageReqVO) { | ||
| 79 | - return knowledgeMapper.selectPage(userId, pageReqVO); | 85 | + public PageResult<AiKnowledgeDO> getKnowledgePage(AiKnowledgePageReqVO pageReqVO) { |
| 86 | + return knowledgeMapper.selectPage(pageReqVO); | ||
| 80 | } | 87 | } |
| 81 | 88 | ||
| 82 | @Override | 89 | @Override |
| 83 | - public VectorStore getVectorStoreById(Long id) { | ||
| 84 | - AiKnowledgeDO knowledge = validateKnowledgeExists(id); | ||
| 85 | - AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId()); | ||
| 86 | - // 创建或获取 VectorStore 对象 | ||
| 87 | - return apiKeyService.getOrCreateVectorStore(model.getKeyId()); | 90 | + public List<AiKnowledgeDO> getKnowledgeSimpleListByStatus(Integer status) { |
| 91 | + return knowledgeMapper.selectListByStatus(status); | ||
| 88 | } | 92 | } |
| 89 | 93 | ||
| 90 | } | 94 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.service.knowledge.bo; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import javax.validation.constraints.NotNull; | ||
| 6 | + | ||
| 7 | +import jakarta.validation.constraints.NotEmpty; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * AI 知识库段落搜索 Request BO | ||
| 11 | + * | ||
| 12 | + * @author 芋道源码 | ||
| 13 | + */ | ||
| 14 | +@Data | ||
| 15 | +public class AiKnowledgeSegmentSearchReqBO { | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 知识库编号 | ||
| 19 | + */ | ||
| 20 | + @NotNull(message = "知识库编号不能为空") | ||
| 21 | + private Long knowledgeId; | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * 内容 | ||
| 25 | + */ | ||
| 26 | + @NotEmpty(message = "内容不能为空") | ||
| 27 | + private String content; | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * 最大返回数量 | ||
| 31 | + */ | ||
| 32 | + private Integer topK; | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 相似度阈值 | ||
| 36 | + */ | ||
| 37 | + private Double similarityThreshold; | ||
| 38 | + | ||
| 39 | +} | ||
| 0 | \ No newline at end of file | 40 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchRespBO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.service.knowledge.bo; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * AI 知识库段落搜索 Response BO | ||
| 7 | + * | ||
| 8 | + * @author 芋道源码 | ||
| 9 | + */ | ||
| 10 | +@Data | ||
| 11 | +public class AiKnowledgeSegmentSearchRespBO { | ||
| 12 | + | ||
| 13 | + /** | ||
| 14 | + * 段落编号 | ||
| 15 | + */ | ||
| 16 | + private Long id; | ||
| 17 | + /** | ||
| 18 | + * 文档编号 | ||
| 19 | + */ | ||
| 20 | + private Long documentId; | ||
| 21 | + /** | ||
| 22 | + * 知识库编号 | ||
| 23 | + */ | ||
| 24 | + private Long knowledgeId; | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 内容 | ||
| 28 | + */ | ||
| 29 | + private String content; | ||
| 30 | + /** | ||
| 31 | + * 内容长度 | ||
| 32 | + */ | ||
| 33 | + private Integer contentLength; | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * Token 数量 | ||
| 37 | + */ | ||
| 38 | + private Integer tokens; | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 相似度分数 | ||
| 42 | + */ | ||
| 43 | + private Double score; | ||
| 44 | + | ||
| 45 | +} | ||
| 0 | \ No newline at end of file | 46 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java
| 1 | package cn.iocoder.yudao.module.ai.service.mindmap; | 1 | package cn.iocoder.yudao.module.ai.service.mindmap; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | -import cn.hutool.core.lang.Assert; | 4 | +import cn.hutool.core.util.ObjUtil; |
| 5 | import cn.hutool.core.util.StrUtil; | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum; | ||
| 6 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | 7 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; |
| 7 | import cn.iocoder.yudao.framework.ai.core.util.AiUtils; | 8 | import cn.iocoder.yudao.framework.ai.core.util.AiUtils; |
| 8 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 9 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| @@ -12,14 +13,13 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | @@ -12,14 +13,13 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | ||
| 12 | import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO; | 13 | import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO; |
| 13 | import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO; | 14 | import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO; |
| 14 | import cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO; | 15 | import cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO; |
| 15 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | ||
| 16 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | 16 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; |
| 17 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; | ||
| 17 | import cn.iocoder.yudao.module.ai.dal.mysql.mindmap.AiMindMapMapper; | 18 | import cn.iocoder.yudao.module.ai.dal.mysql.mindmap.AiMindMapMapper; |
| 18 | import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum; | 19 | import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum; |
| 19 | import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants; | 20 | import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants; |
| 20 | -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; | ||
| 21 | -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; | ||
| 22 | import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; | 21 | import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; |
| 22 | +import cn.iocoder.yudao.module.ai.service.model.AiModelService; | ||
| 23 | import jakarta.annotation.Resource; | 23 | import jakarta.annotation.Resource; |
| 24 | import lombok.extern.slf4j.Slf4j; | 24 | import lombok.extern.slf4j.Slf4j; |
| 25 | import org.springframework.ai.chat.messages.Message; | 25 | import org.springframework.ai.chat.messages.Message; |
| @@ -38,7 +38,7 @@ import java.util.List; | @@ -38,7 +38,7 @@ import java.util.List; | ||
| 38 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 38 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 39 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; | 39 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; |
| 40 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 40 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 41 | -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MIND_MAP_NOT_EXISTS; | 41 | +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; |
| 42 | 42 | ||
| 43 | /** | 43 | /** |
| 44 | * AI 思维导图 Service 实现类 | 44 | * AI 思维导图 Service 实现类 |
| @@ -50,9 +50,7 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MIND_MAP_NOT_E | @@ -50,9 +50,7 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MIND_MAP_NOT_E | ||
| 50 | public class AiMindMapServiceImpl implements AiMindMapService { | 50 | public class AiMindMapServiceImpl implements AiMindMapService { |
| 51 | 51 | ||
| 52 | @Resource | 52 | @Resource |
| 53 | - private AiApiKeyService apiKeyService; | ||
| 54 | - @Resource | ||
| 55 | - private AiChatModelService chatModalService; | 53 | + private AiModelService modalService; |
| 56 | @Resource | 54 | @Resource |
| 57 | private AiChatRoleService chatRoleService; | 55 | private AiChatRoleService chatRoleService; |
| 58 | 56 | ||
| @@ -65,17 +63,17 @@ public class AiMindMapServiceImpl implements AiMindMapService { | @@ -65,17 +63,17 @@ public class AiMindMapServiceImpl implements AiMindMapService { | ||
| 65 | AiChatRoleDO role = CollUtil.getFirst( | 63 | AiChatRoleDO role = CollUtil.getFirst( |
| 66 | chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_MIND_MAP_ROLE.getName())); | 64 | chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_MIND_MAP_ROLE.getName())); |
| 67 | // 1.1 获取导图执行模型 | 65 | // 1.1 获取导图执行模型 |
| 68 | - AiChatModelDO model = getModel(role); | 66 | + AiModelDO model = getModel(role); |
| 69 | // 1.2 获取角色设定消息 | 67 | // 1.2 获取角色设定消息 |
| 70 | String systemMessage = role != null && StrUtil.isNotBlank(role.getSystemMessage()) | 68 | String systemMessage = role != null && StrUtil.isNotBlank(role.getSystemMessage()) |
| 71 | ? role.getSystemMessage() : AiChatRoleEnum.AI_MIND_MAP_ROLE.getSystemMessage(); | 69 | ? role.getSystemMessage() : AiChatRoleEnum.AI_MIND_MAP_ROLE.getSystemMessage(); |
| 72 | // 1.3 校验平台 | 70 | // 1.3 校验平台 |
| 73 | AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); | 71 | AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); |
| 74 | - ChatModel chatModel = apiKeyService.getChatModel(model.getKeyId()); | 72 | + ChatModel chatModel = modalService.getChatModel(model.getId()); |
| 75 | 73 | ||
| 76 | // 2. 插入思维导图信息 | 74 | // 2. 插入思维导图信息 |
| 77 | - AiMindMapDO mindMapDO = BeanUtils.toBean(generateReqVO, AiMindMapDO.class, | ||
| 78 | - mindMap -> mindMap.setUserId(userId).setModel(model.getModel()).setPlatform(platform.getPlatform())); | 75 | + AiMindMapDO mindMapDO = BeanUtils.toBean(generateReqVO, AiMindMapDO.class, mindMap -> mindMap.setUserId(userId) |
| 76 | + .setPlatform(platform.getPlatform()).setModelId(model.getId()).setModel(model.getModel())); | ||
| 79 | mindMapMapper.insert(mindMapDO); | 77 | mindMapMapper.insert(mindMapDO); |
| 80 | 78 | ||
| 81 | // 3.1 构建 Prompt,并进行调用 | 79 | // 3.1 构建 Prompt,并进行调用 |
| @@ -85,7 +83,7 @@ public class AiMindMapServiceImpl implements AiMindMapService { | @@ -85,7 +83,7 @@ public class AiMindMapServiceImpl implements AiMindMapService { | ||
| 85 | // 3.2 流式返回 | 83 | // 3.2 流式返回 |
| 86 | StringBuffer contentBuffer = new StringBuffer(); | 84 | StringBuffer contentBuffer = new StringBuffer(); |
| 87 | return streamResponse.map(chunk -> { | 85 | return streamResponse.map(chunk -> { |
| 88 | - String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null; | 86 | + String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null; |
| 89 | newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况 | 87 | newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况 |
| 90 | contentBuffer.append(newContent); | 88 | contentBuffer.append(newContent); |
| 91 | // 响应结果 | 89 | // 响应结果 |
| @@ -103,7 +101,7 @@ public class AiMindMapServiceImpl implements AiMindMapService { | @@ -103,7 +101,7 @@ public class AiMindMapServiceImpl implements AiMindMapService { | ||
| 103 | 101 | ||
| 104 | } | 102 | } |
| 105 | 103 | ||
| 106 | - private Prompt buildPrompt(AiMindMapGenerateReqVO generateReqVO, AiChatModelDO model, String systemMessage) { | 104 | + private Prompt buildPrompt(AiMindMapGenerateReqVO generateReqVO, AiModelDO model, String systemMessage) { |
| 107 | // 1. 构建 message 列表 | 105 | // 1. 构建 message 列表 |
| 108 | List<Message> chatMessages = buildMessages(generateReqVO, systemMessage); | 106 | List<Message> chatMessages = buildMessages(generateReqVO, systemMessage); |
| 109 | // 2. 构建 options 对象 | 107 | // 2. 构建 options 对象 |
| @@ -123,15 +121,21 @@ public class AiMindMapServiceImpl implements AiMindMapService { | @@ -123,15 +121,21 @@ public class AiMindMapServiceImpl implements AiMindMapService { | ||
| 123 | return chatMessages; | 121 | return chatMessages; |
| 124 | } | 122 | } |
| 125 | 123 | ||
| 126 | - private AiChatModelDO getModel(AiChatRoleDO role) { | ||
| 127 | - AiChatModelDO model = null; | 124 | + private AiModelDO getModel(AiChatRoleDO role) { |
| 125 | + AiModelDO model = null; | ||
| 128 | if (role != null && role.getModelId() != null) { | 126 | if (role != null && role.getModelId() != null) { |
| 129 | - model = chatModalService.getChatModel(role.getModelId()); | 127 | + model = modalService.getModel(role.getModelId()); |
| 128 | + } | ||
| 129 | + if (model == null) { | ||
| 130 | + model = modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType()); | ||
| 130 | } | 131 | } |
| 132 | + // 校验模型存在、且合法 | ||
| 131 | if (model == null) { | 133 | if (model == null) { |
| 132 | - model = chatModalService.getRequiredDefaultChatModel(); | 134 | + throw exception(MODEL_NOT_EXISTS); |
| 135 | + } | ||
| 136 | + if (ObjUtil.notEqual(model.getType(), AiModelTypeEnum.CHAT.getType())) { | ||
| 137 | + throw exception(MODEL_USE_TYPE_ERROR); | ||
| 133 | } | 138 | } |
| 134 | - Assert.notNull(model, "[AI] 获取不到模型"); | ||
| 135 | return model; | 139 | return model; |
| 136 | } | 140 | } |
| 137 | 141 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java
| 1 | package cn.iocoder.yudao.module.ai.service.model; | 1 | package cn.iocoder.yudao.module.ai.service.model; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | ||
| 4 | -import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | ||
| 5 | -import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; | ||
| 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO; | 4 | import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO; |
| 8 | import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO; | 5 | import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO; |
| 9 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; | 6 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; |
| 10 | import jakarta.validation.Valid; | 7 | import jakarta.validation.Valid; |
| 11 | -import org.springframework.ai.chat.model.ChatModel; | ||
| 12 | -import org.springframework.ai.embedding.EmbeddingModel; | ||
| 13 | -import org.springframework.ai.image.ImageModel; | ||
| 14 | -import org.springframework.ai.vectorstore.VectorStore; | ||
| 15 | 8 | ||
| 16 | import java.util.List; | 9 | import java.util.List; |
| 17 | 10 | ||
| @@ -75,58 +68,13 @@ public interface AiApiKeyService { | @@ -75,58 +68,13 @@ public interface AiApiKeyService { | ||
| 75 | */ | 68 | */ |
| 76 | List<AiApiKeyDO> getApiKeyList(); | 69 | List<AiApiKeyDO> getApiKeyList(); |
| 77 | 70 | ||
| 78 | - // ========== 与 spring-ai 集成 ========== | ||
| 79 | - | ||
| 80 | /** | 71 | /** |
| 81 | - * 获得 ChatModel 对象 | ||
| 82 | - * | ||
| 83 | - * @param id 编号 | ||
| 84 | - * @return ChatModel 对象 | ||
| 85 | - */ | ||
| 86 | - ChatModel getChatModel(Long id); | ||
| 87 | - | ||
| 88 | - /** | ||
| 89 | - * 获得 ImageModel 对象 | ||
| 90 | - * | ||
| 91 | - * TODO 可优化点:目前默认获取 platform 对应的第一个开启的配置用于绘画;后续可以支持配置选择 | 72 | + * 获得默认的 API 密钥 |
| 92 | * | 73 | * |
| 93 | * @param platform 平台 | 74 | * @param platform 平台 |
| 94 | - * @return ImageModel 对象 | ||
| 95 | - */ | ||
| 96 | - ImageModel getImageModel(AiPlatformEnum platform); | ||
| 97 | - | ||
| 98 | - /** | ||
| 99 | - * 获得 MidjourneyApi 对象 | ||
| 100 | - * | ||
| 101 | - * TODO 可优化点:目前默认获取 Midjourney 对应的第一个开启的配置用于绘画;后续可以支持配置选择 | ||
| 102 | - * | ||
| 103 | - * @return MidjourneyApi 对象 | ||
| 104 | - */ | ||
| 105 | - MidjourneyApi getMidjourneyApi(); | ||
| 106 | - | ||
| 107 | - /** | ||
| 108 | - * 获得 SunoApi 对象 | ||
| 109 | - * | ||
| 110 | - * TODO 可优化点:目前默认获取 Suno 对应的第一个开启的配置用于音乐;后续可以支持配置选择 | ||
| 111 | - * | ||
| 112 | - * @return SunoApi 对象 | ||
| 113 | - */ | ||
| 114 | - SunoApi getSunoApi(); | ||
| 115 | - | ||
| 116 | - /** | ||
| 117 | - * 获得 EmbeddingModel 对象 | ||
| 118 | - * | ||
| 119 | - * @param id 编号 | ||
| 120 | - * @return EmbeddingModel 对象 | ||
| 121 | - */ | ||
| 122 | - EmbeddingModel getEmbeddingModel(Long id); | ||
| 123 | - | ||
| 124 | - /** | ||
| 125 | - * 获得 VectorStore 对象 | ||
| 126 | - * | ||
| 127 | - * @param id 编号 | ||
| 128 | - * @return VectorStore 对象 | 75 | + * @param status 状态 |
| 76 | + * @return API 密钥 | ||
| 129 | */ | 77 | */ |
| 130 | - VectorStore getOrCreateVectorStore(Long id); | 78 | + AiApiKeyDO getRequiredDefaultApiKey(String platform, Integer status); |
| 131 | 79 | ||
| 132 | } | 80 | } |
| 133 | \ No newline at end of file | 81 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
| 1 | package cn.iocoder.yudao.module.ai.service.model; | 1 | package cn.iocoder.yudao.module.ai.service.model; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | ||
| 4 | -import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; | ||
| 5 | -import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | ||
| 6 | -import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; | ||
| 7 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 8 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 9 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 5 | 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 | @@ -12,17 +8,14 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveR | ||
| 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; | 8 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; |
| 13 | import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper; | 9 | import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper; |
| 14 | import jakarta.annotation.Resource; | 10 | import jakarta.annotation.Resource; |
| 15 | -import org.springframework.ai.chat.model.ChatModel; | ||
| 16 | -import org.springframework.ai.embedding.EmbeddingModel; | ||
| 17 | -import org.springframework.ai.image.ImageModel; | ||
| 18 | -import org.springframework.ai.vectorstore.VectorStore; | ||
| 19 | import org.springframework.stereotype.Service; | 11 | import org.springframework.stereotype.Service; |
| 20 | import org.springframework.validation.annotation.Validated; | 12 | import org.springframework.validation.annotation.Validated; |
| 21 | 13 | ||
| 22 | import java.util.List; | 14 | import java.util.List; |
| 23 | 15 | ||
| 24 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 16 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 25 | -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; | 17 | +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_KEY_DISABLE; |
| 18 | +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_KEY_NOT_EXISTS; | ||
| 26 | 19 | ||
| 27 | /** | 20 | /** |
| 28 | * AI API 密钥 Service 实现类 | 21 | * AI API 密钥 Service 实现类 |
| @@ -36,9 +29,6 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { | @@ -36,9 +29,6 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { | ||
| 36 | @Resource | 29 | @Resource |
| 37 | private AiApiKeyMapper apiKeyMapper; | 30 | private AiApiKeyMapper apiKeyMapper; |
| 38 | 31 | ||
| 39 | - @Resource | ||
| 40 | - private AiModelFactory modelFactory; | ||
| 41 | - | ||
| 42 | @Override | 32 | @Override |
| 43 | public Long createApiKey(AiApiKeySaveReqVO createReqVO) { | 33 | public Long createApiKey(AiApiKeySaveReqVO createReqVO) { |
| 44 | // 插入 | 34 | // 插入 |
| @@ -97,57 +87,13 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { | @@ -97,57 +87,13 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { | ||
| 97 | return apiKeyMapper.selectList(); | 87 | return apiKeyMapper.selectList(); |
| 98 | } | 88 | } |
| 99 | 89 | ||
| 100 | - // ========== 与 spring-ai 集成 ========== | ||
| 101 | - | ||
| 102 | - @Override | ||
| 103 | - public ChatModel getChatModel(Long id) { | ||
| 104 | - AiApiKeyDO apiKey = validateApiKey(id); | ||
| 105 | - AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); | ||
| 106 | - return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl()); | ||
| 107 | - } | ||
| 108 | - | ||
| 109 | - @Override | ||
| 110 | - public ImageModel getImageModel(AiPlatformEnum platform) { | ||
| 111 | - AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform.getPlatform(), CommonStatusEnum.ENABLE.getStatus()); | ||
| 112 | - if (apiKey == null) { | ||
| 113 | - throw exception(API_KEY_IMAGE_NODE_FOUND, platform.getName()); | ||
| 114 | - } | ||
| 115 | - return modelFactory.getOrCreateImageModel(platform, apiKey.getApiKey(), apiKey.getUrl()); | ||
| 116 | - } | ||
| 117 | - | ||
| 118 | - @Override | ||
| 119 | - public MidjourneyApi getMidjourneyApi() { | ||
| 120 | - AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus( | ||
| 121 | - AiPlatformEnum.MIDJOURNEY.getPlatform(), CommonStatusEnum.ENABLE.getStatus()); | ||
| 122 | - if (apiKey == null) { | ||
| 123 | - throw exception(API_KEY_MIDJOURNEY_NOT_FOUND); | ||
| 124 | - } | ||
| 125 | - return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl()); | ||
| 126 | - } | ||
| 127 | - | ||
| 128 | @Override | 90 | @Override |
| 129 | - public SunoApi getSunoApi() { | ||
| 130 | - AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus( | ||
| 131 | - AiPlatformEnum.SUNO.getPlatform(), CommonStatusEnum.ENABLE.getStatus()); | 91 | + public AiApiKeyDO getRequiredDefaultApiKey(String platform, Integer status) { |
| 92 | + AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform, status); | ||
| 132 | if (apiKey == null) { | 93 | if (apiKey == null) { |
| 133 | - throw exception(API_KEY_SUNO_NOT_FOUND); | 94 | + throw exception(API_KEY_NOT_EXISTS); |
| 134 | } | 95 | } |
| 135 | - return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl()); | ||
| 136 | - } | ||
| 137 | - | ||
| 138 | - @Override | ||
| 139 | - public EmbeddingModel getEmbeddingModel(Long id) { | ||
| 140 | - AiApiKeyDO apiKey = validateApiKey(id); | ||
| 141 | - AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); | ||
| 142 | - return modelFactory.getOrCreateEmbeddingModel(platform, apiKey.getApiKey(), apiKey.getUrl()); | ||
| 143 | - } | ||
| 144 | - | ||
| 145 | - @Override | ||
| 146 | - public VectorStore getOrCreateVectorStore(Long id) { | ||
| 147 | - AiApiKeyDO apiKey = validateApiKey(id); | ||
| 148 | - AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); | ||
| 149 | - // 创建或获取 VectorStore 对象 | ||
| 150 | - return modelFactory.getOrCreateVectorStore(getEmbeddingModel(id), platform, apiKey.getApiKey(), apiKey.getUrl()); | 96 | + return apiKey; |
| 151 | } | 97 | } |
| 152 | 98 | ||
| 153 | } | 99 | } |
| 154 | \ No newline at end of file | 100 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.service.model; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | ||
| 4 | -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 5 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 6 | -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 7 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO; | ||
| 8 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO; | ||
| 9 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | ||
| 10 | -import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatModelMapper; | ||
| 11 | -import jakarta.annotation.Resource; | ||
| 12 | -import org.springframework.stereotype.Service; | ||
| 13 | -import org.springframework.validation.annotation.Validated; | ||
| 14 | - | ||
| 15 | -import java.util.Collection; | ||
| 16 | -import java.util.List; | ||
| 17 | - | ||
| 18 | -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 19 | -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; | ||
| 20 | - | ||
| 21 | -/** | ||
| 22 | - * AI 聊天模型 Service 实现类 | ||
| 23 | - * | ||
| 24 | - * @author fansili | ||
| 25 | - */ | ||
| 26 | -@Service | ||
| 27 | -@Validated | ||
| 28 | -public class AiChatModelServiceImpl implements AiChatModelService { | ||
| 29 | - | ||
| 30 | - @Resource | ||
| 31 | - private AiApiKeyService apiKeyService; | ||
| 32 | - | ||
| 33 | - @Resource | ||
| 34 | - private AiChatModelMapper chatModelMapper; | ||
| 35 | - | ||
| 36 | - @Override | ||
| 37 | - public Long createChatModel(AiChatModelSaveReqVO createReqVO) { | ||
| 38 | - // 1. 校验 | ||
| 39 | - AiPlatformEnum.validatePlatform(createReqVO.getPlatform()); | ||
| 40 | - apiKeyService.validateApiKey(createReqVO.getKeyId()); | ||
| 41 | - | ||
| 42 | - // 2. 插入 | ||
| 43 | - AiChatModelDO chatModel = BeanUtils.toBean(createReqVO, AiChatModelDO.class); | ||
| 44 | - chatModelMapper.insert(chatModel); | ||
| 45 | - return chatModel.getId(); | ||
| 46 | - } | ||
| 47 | - | ||
| 48 | - @Override | ||
| 49 | - public void updateChatModel(AiChatModelSaveReqVO updateReqVO) { | ||
| 50 | - // 1. 校验 | ||
| 51 | - validateChatModelExists(updateReqVO.getId()); | ||
| 52 | - AiPlatformEnum.validatePlatform(updateReqVO.getPlatform()); | ||
| 53 | - apiKeyService.validateApiKey(updateReqVO.getKeyId()); | ||
| 54 | - | ||
| 55 | - // 2. 更新 | ||
| 56 | - AiChatModelDO updateObj = BeanUtils.toBean(updateReqVO, AiChatModelDO.class); | ||
| 57 | - chatModelMapper.updateById(updateObj); | ||
| 58 | - } | ||
| 59 | - | ||
| 60 | - @Override | ||
| 61 | - public void deleteChatModel(Long id) { | ||
| 62 | - // 校验存在 | ||
| 63 | - validateChatModelExists(id); | ||
| 64 | - // 删除 | ||
| 65 | - chatModelMapper.deleteById(id); | ||
| 66 | - } | ||
| 67 | - | ||
| 68 | - private AiChatModelDO validateChatModelExists(Long id) { | ||
| 69 | - AiChatModelDO model = chatModelMapper.selectById(id); | ||
| 70 | - if (chatModelMapper.selectById(id) == null) { | ||
| 71 | - throw exception(CHAT_MODEL_NOT_EXISTS); | ||
| 72 | - } | ||
| 73 | - return model; | ||
| 74 | - } | ||
| 75 | - | ||
| 76 | - @Override | ||
| 77 | - public AiChatModelDO getChatModel(Long id) { | ||
| 78 | - return chatModelMapper.selectById(id); | ||
| 79 | - } | ||
| 80 | - | ||
| 81 | - @Override | ||
| 82 | - public AiChatModelDO getRequiredDefaultChatModel() { | ||
| 83 | - AiChatModelDO model = chatModelMapper.selectFirstByStatus(CommonStatusEnum.ENABLE.getStatus()); | ||
| 84 | - if (model == null) { | ||
| 85 | - throw exception(CHAT_MODEL_DEFAULT_NOT_EXISTS); | ||
| 86 | - } | ||
| 87 | - return model; | ||
| 88 | - } | ||
| 89 | - | ||
| 90 | - @Override | ||
| 91 | - public PageResult<AiChatModelDO> getChatModelPage(AiChatModelPageReqVO pageReqVO) { | ||
| 92 | - return chatModelMapper.selectPage(pageReqVO); | ||
| 93 | - } | ||
| 94 | - | ||
| 95 | - @Override | ||
| 96 | - public AiChatModelDO validateChatModel(Long id) { | ||
| 97 | - AiChatModelDO model = validateChatModelExists(id); | ||
| 98 | - if (CommonStatusEnum.isDisable(model.getStatus())) { | ||
| 99 | - throw exception(CHAT_MODEL_DISABLE); | ||
| 100 | - } | ||
| 101 | - return model; | ||
| 102 | - } | ||
| 103 | - | ||
| 104 | - @Override | ||
| 105 | - public List<AiChatModelDO> getChatModelListByStatus(Integer status) { | ||
| 106 | - return chatModelMapper.selectList(status); | ||
| 107 | - } | ||
| 108 | - | ||
| 109 | - @Override | ||
| 110 | - public List<AiChatModelDO> getChatModelList(Collection<Long> ids) { | ||
| 111 | - return chatModelMapper.selectBatchIds(ids); | ||
| 112 | - } | ||
| 113 | - | ||
| 114 | -} | ||
| 115 | \ No newline at end of file | 0 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleService.java
| @@ -32,7 +32,7 @@ public interface AiChatRoleService { | @@ -32,7 +32,7 @@ public interface AiChatRoleService { | ||
| 32 | * 创建【我的】聊天角色 | 32 | * 创建【我的】聊天角色 |
| 33 | * | 33 | * |
| 34 | * @param createReqVO 创建信息 | 34 | * @param createReqVO 创建信息 |
| 35 | - * @param userId 用户编号 | 35 | + * @param userId 用户编号 |
| 36 | * @return 编号 | 36 | * @return 编号 |
| 37 | */ | 37 | */ |
| 38 | Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId); | 38 | Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId); |
| @@ -48,7 +48,7 @@ public interface AiChatRoleService { | @@ -48,7 +48,7 @@ public interface AiChatRoleService { | ||
| 48 | * 创建【我的】聊天角色 | 48 | * 创建【我的】聊天角色 |
| 49 | * | 49 | * |
| 50 | * @param updateReqVO 更新信息 | 50 | * @param updateReqVO 更新信息 |
| 51 | - * @param userId 用户编号 | 51 | + * @param userId 用户编号 |
| 52 | */ | 52 | */ |
| 53 | void updateChatRoleMy(AiChatRoleSaveMyReqVO updateReqVO, Long userId); | 53 | void updateChatRoleMy(AiChatRoleSaveMyReqVO updateReqVO, Long userId); |
| 54 | 54 | ||
| @@ -62,7 +62,7 @@ public interface AiChatRoleService { | @@ -62,7 +62,7 @@ public interface AiChatRoleService { | ||
| 62 | /** | 62 | /** |
| 63 | * 删除【我的】聊天角色 | 63 | * 删除【我的】聊天角色 |
| 64 | * | 64 | * |
| 65 | - * @param id 编号 | 65 | + * @param id 编号 |
| 66 | * @param userId 用户编号 | 66 | * @param userId 用户编号 |
| 67 | */ | 67 | */ |
| 68 | void deleteChatRoleMy(Long id, Long userId); | 68 | void deleteChatRoleMy(Long id, Long userId); |
| @@ -106,7 +106,7 @@ public interface AiChatRoleService { | @@ -106,7 +106,7 @@ public interface AiChatRoleService { | ||
| 106 | * 获得【我的】聊天角色分页 | 106 | * 获得【我的】聊天角色分页 |
| 107 | * | 107 | * |
| 108 | * @param pageReqVO 分页查询 | 108 | * @param pageReqVO 分页查询 |
| 109 | - * @param userId 用户编号 | 109 | + * @param userId 用户编号 |
| 110 | * @return 聊天角色分页 | 110 | * @return 聊天角色分页 |
| 111 | */ | 111 | */ |
| 112 | PageResult<AiChatRoleDO> getChatRoleMyPage(AiChatRolePageReqVO pageReqVO, Long userId); | 112 | PageResult<AiChatRoleDO> getChatRoleMyPage(AiChatRolePageReqVO pageReqVO, Long userId); |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java
| @@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleS | @@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleS | ||
| 11 | import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO; | 11 | import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO; |
| 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; |
| 13 | import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatRoleMapper; | 13 | import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatRoleMapper; |
| 14 | +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService; | ||
| 14 | import jakarta.annotation.Resource; | 15 | import jakarta.annotation.Resource; |
| 15 | import lombok.extern.slf4j.Slf4j; | 16 | import lombok.extern.slf4j.Slf4j; |
| 16 | import org.springframework.stereotype.Service; | 17 | import org.springframework.stereotype.Service; |
| @@ -21,7 +22,8 @@ import java.util.List; | @@ -21,7 +22,8 @@ import java.util.List; | ||
| 21 | 22 | ||
| 22 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 23 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 23 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | 24 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| 24 | -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; | 25 | +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_ROLE_DISABLE; |
| 26 | +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_ROLE_NOT_EXISTS; | ||
| 25 | 27 | ||
| 26 | /** | 28 | /** |
| 27 | * AI 聊天角色 Service 实现类 | 29 | * AI 聊天角色 Service 实现类 |
| @@ -35,8 +37,19 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { | @@ -35,8 +37,19 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { | ||
| 35 | @Resource | 37 | @Resource |
| 36 | private AiChatRoleMapper chatRoleMapper; | 38 | private AiChatRoleMapper chatRoleMapper; |
| 37 | 39 | ||
| 40 | + @Resource | ||
| 41 | + private AiKnowledgeService knowledgeService; | ||
| 42 | + @Resource | ||
| 43 | + private AiToolService toolService; | ||
| 44 | + | ||
| 38 | @Override | 45 | @Override |
| 39 | public Long createChatRole(AiChatRoleSaveReqVO createReqVO) { | 46 | public Long createChatRole(AiChatRoleSaveReqVO createReqVO) { |
| 47 | + // 校验文档 | ||
| 48 | + validateDocuments(createReqVO.getKnowledgeIds()); | ||
| 49 | + // 校验工具 | ||
| 50 | + validateTools(createReqVO.getToolIds()); | ||
| 51 | + | ||
| 52 | + // 保存角色 | ||
| 40 | AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class); | 53 | AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class); |
| 41 | chatRoleMapper.insert(chatRole); | 54 | chatRoleMapper.insert(chatRole); |
| 42 | return chatRole.getId(); | 55 | return chatRole.getId(); |
| @@ -44,6 +57,12 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { | @@ -44,6 +57,12 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { | ||
| 44 | 57 | ||
| 45 | @Override | 58 | @Override |
| 46 | public Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId) { | 59 | public Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId) { |
| 60 | + // 校验文档 | ||
| 61 | + validateDocuments(createReqVO.getKnowledgeIds()); | ||
| 62 | + // 校验工具 | ||
| 63 | + validateTools(createReqVO.getToolIds()); | ||
| 64 | + | ||
| 65 | + // 保存角色 | ||
| 47 | AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class).setUserId(userId) | 66 | AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class).setUserId(userId) |
| 48 | .setStatus(CommonStatusEnum.ENABLE.getStatus()).setPublicStatus(false); | 67 | .setStatus(CommonStatusEnum.ENABLE.getStatus()).setPublicStatus(false); |
| 49 | chatRoleMapper.insert(chatRole); | 68 | chatRoleMapper.insert(chatRole); |
| @@ -54,7 +73,12 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { | @@ -54,7 +73,12 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { | ||
| 54 | public void updateChatRole(AiChatRoleSaveReqVO updateReqVO) { | 73 | public void updateChatRole(AiChatRoleSaveReqVO updateReqVO) { |
| 55 | // 校验存在 | 74 | // 校验存在 |
| 56 | validateChatRoleExists(updateReqVO.getId()); | 75 | validateChatRoleExists(updateReqVO.getId()); |
| 57 | - // 更新 | 76 | + // 校验文档 |
| 77 | + validateDocuments(updateReqVO.getKnowledgeIds()); | ||
| 78 | + // 校验工具 | ||
| 79 | + validateTools(updateReqVO.getToolIds()); | ||
| 80 | + | ||
| 81 | + // 更新角色 | ||
| 58 | AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class); | 82 | AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class); |
| 59 | chatRoleMapper.updateById(updateObj); | 83 | chatRoleMapper.updateById(updateObj); |
| 60 | } | 84 | } |
| @@ -66,12 +90,42 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { | @@ -66,12 +90,42 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { | ||
| 66 | if (ObjectUtil.notEqual(chatRole.getUserId(), userId)) { | 90 | if (ObjectUtil.notEqual(chatRole.getUserId(), userId)) { |
| 67 | throw exception(CHAT_ROLE_NOT_EXISTS); | 91 | throw exception(CHAT_ROLE_NOT_EXISTS); |
| 68 | } | 92 | } |
| 93 | + // 校验文档 | ||
| 94 | + validateDocuments(updateReqVO.getKnowledgeIds()); | ||
| 95 | + // 校验工具 | ||
| 96 | + validateTools(updateReqVO.getToolIds()); | ||
| 69 | 97 | ||
| 70 | // 更新 | 98 | // 更新 |
| 71 | AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class); | 99 | AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class); |
| 72 | chatRoleMapper.updateById(updateObj); | 100 | chatRoleMapper.updateById(updateObj); |
| 73 | } | 101 | } |
| 74 | 102 | ||
| 103 | + /** | ||
| 104 | + * 校验知识库是否存在 | ||
| 105 | + * | ||
| 106 | + * @param knowledgeIds 知识库编号列表 | ||
| 107 | + */ | ||
| 108 | + private void validateDocuments(List<Long> knowledgeIds) { | ||
| 109 | + if (CollUtil.isEmpty(knowledgeIds)) { | ||
| 110 | + return; | ||
| 111 | + } | ||
| 112 | + // 校验文档是否存在 | ||
| 113 | + knowledgeIds.forEach(knowledgeService::validateKnowledgeExists); | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + /** | ||
| 117 | + * 校验工具是否存在 | ||
| 118 | + * | ||
| 119 | + * @param toolIds 工具编号列表 | ||
| 120 | + */ | ||
| 121 | + private void validateTools(List<Long> toolIds) { | ||
| 122 | + if (CollUtil.isEmpty(toolIds)) { | ||
| 123 | + return; | ||
| 124 | + } | ||
| 125 | + // 遍历校验每个工具是否存在 | ||
| 126 | + toolIds.forEach(toolService::validateToolExists); | ||
| 127 | + } | ||
| 128 | + | ||
| 75 | @Override | 129 | @Override |
| 76 | public void deleteChatRole(Long id) { | 130 | public void deleteChatRole(Long id) { |
| 77 | // 校验存在 | 131 | // 校验存在 |
| @@ -134,7 +188,8 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { | @@ -134,7 +188,8 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { | ||
| 134 | @Override | 188 | @Override |
| 135 | public List<String> getChatRoleCategoryList() { | 189 | public List<String> getChatRoleCategoryList() { |
| 136 | List<AiChatRoleDO> list = chatRoleMapper.selectListGroupByCategory(CommonStatusEnum.ENABLE.getStatus()); | 190 | List<AiChatRoleDO> list = chatRoleMapper.selectListGroupByCategory(CommonStatusEnum.ENABLE.getStatus()); |
| 137 | - return convertList(list, AiChatRoleDO::getCategory, role -> role != null && StrUtil.isNotBlank(role.getCategory())); | 191 | + return convertList(list, AiChatRoleDO::getCategory, |
| 192 | + role -> role != null && StrUtil.isNotBlank(role.getCategory())); | ||
| 138 | } | 193 | } |
| 139 | 194 | ||
| 140 | @Override | 195 | @Override |
| @@ -143,4 +198,3 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { | @@ -143,4 +198,3 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { | ||
| 143 | } | 198 | } |
| 144 | 199 | ||
| 145 | } | 200 | } |
| 146 | - |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.service.model; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | ||
| 4 | +import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; | ||
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 6 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO; | ||
| 8 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; | ||
| 9 | +import jakarta.validation.Valid; | ||
| 10 | +import org.springframework.ai.chat.model.ChatModel; | ||
| 11 | +import org.springframework.ai.image.ImageModel; | ||
| 12 | +import org.springframework.ai.vectorstore.VectorStore; | ||
| 13 | + | ||
| 14 | +import javax.annotation.Nullable; | ||
| 15 | +import java.util.List; | ||
| 16 | +import java.util.Map; | ||
| 17 | + | ||
| 18 | +/** | ||
| 19 | + * AI 模型 Service 接口 | ||
| 20 | + * | ||
| 21 | + * @author fansili | ||
| 22 | + * @since 2024/4/24 19:42 | ||
| 23 | + */ | ||
| 24 | +public interface AiModelService { | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 创建模型 | ||
| 28 | + * | ||
| 29 | + * @param createReqVO 创建信息 | ||
| 30 | + * @return 编号 | ||
| 31 | + */ | ||
| 32 | + Long createModel(@Valid AiModelSaveReqVO createReqVO); | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 更新模型 | ||
| 36 | + * | ||
| 37 | + * @param updateReqVO 更新信息 | ||
| 38 | + */ | ||
| 39 | + void updateModel(@Valid AiModelSaveReqVO updateReqVO); | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * 删除模型 | ||
| 43 | + * | ||
| 44 | + * @param id 编号 | ||
| 45 | + */ | ||
| 46 | + void deleteModel(Long id); | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * 获得模型 | ||
| 50 | + * | ||
| 51 | + * @param id 编号 | ||
| 52 | + * @return 模型 | ||
| 53 | + */ | ||
| 54 | + AiModelDO getModel(Long id); | ||
| 55 | + | ||
| 56 | + /** | ||
| 57 | + * 获得默认的模型 | ||
| 58 | + * | ||
| 59 | + * 如果获取不到,则抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 业务异常 | ||
| 60 | + * | ||
| 61 | + * @return 模型 | ||
| 62 | + */ | ||
| 63 | + AiModelDO getRequiredDefaultModel(Integer type); | ||
| 64 | + | ||
| 65 | + /** | ||
| 66 | + * 获得模型分页 | ||
| 67 | + * | ||
| 68 | + * @param pageReqVO 分页查询 | ||
| 69 | + * @return 模型分页 | ||
| 70 | + */ | ||
| 71 | + PageResult<AiModelDO> getModelPage(AiModelPageReqVO pageReqVO); | ||
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * 校验模型是否可使用 | ||
| 75 | + * | ||
| 76 | + * @param id 编号 | ||
| 77 | + * @return 模型 | ||
| 78 | + */ | ||
| 79 | + AiModelDO validateModel(Long id); | ||
| 80 | + | ||
| 81 | + /** | ||
| 82 | + * 获得模型列表 | ||
| 83 | + * | ||
| 84 | + * @param status 状态 | ||
| 85 | + * @param type 类型 | ||
| 86 | + * @param platform 平台,允许空 | ||
| 87 | + * @return 模型列表 | ||
| 88 | + */ | ||
| 89 | + List<AiModelDO> getModelListByStatusAndType(Integer status, Integer type, | ||
| 90 | + @Nullable String platform); | ||
| 91 | + | ||
| 92 | + // ========== 与 Spring AI 集成 ========== | ||
| 93 | + | ||
| 94 | + /** | ||
| 95 | + * 获得 ChatModel 对象 | ||
| 96 | + * | ||
| 97 | + * @param id 编号 | ||
| 98 | + * @return ChatModel 对象 | ||
| 99 | + */ | ||
| 100 | + ChatModel getChatModel(Long id); | ||
| 101 | + | ||
| 102 | + /** | ||
| 103 | + * 获得 ImageModel 对象 | ||
| 104 | + * | ||
| 105 | + * @param id 编号 | ||
| 106 | + * @return ImageModel 对象 | ||
| 107 | + */ | ||
| 108 | + ImageModel getImageModel(Long id); | ||
| 109 | + | ||
| 110 | + /** | ||
| 111 | + * 获得 MidjourneyApi 对象 | ||
| 112 | + * | ||
| 113 | + * @param id 编号 | ||
| 114 | + * @return MidjourneyApi 对象 | ||
| 115 | + */ | ||
| 116 | + MidjourneyApi getMidjourneyApi(Long id); | ||
| 117 | + | ||
| 118 | + /** | ||
| 119 | + * 获得 SunoApi 对象 | ||
| 120 | + * | ||
| 121 | + * @return SunoApi 对象 | ||
| 122 | + */ | ||
| 123 | + SunoApi getSunoApi(); | ||
| 124 | + | ||
| 125 | + /** | ||
| 126 | + * 获得 VectorStore 对象 | ||
| 127 | + * | ||
| 128 | + * @param id 编号 | ||
| 129 | + * @param metadataFields 元数据的定义 | ||
| 130 | + * @return VectorStore 对象 | ||
| 131 | + */ | ||
| 132 | + VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields); | ||
| 133 | + | ||
| 134 | +} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.service.model; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; | ||
| 5 | +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | ||
| 6 | +import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; | ||
| 7 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 8 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 9 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 10 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO; | ||
| 11 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO; | ||
| 12 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; | ||
| 13 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; | ||
| 14 | +import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatMapper; | ||
| 15 | +import jakarta.annotation.Resource; | ||
| 16 | +import org.springframework.ai.chat.model.ChatModel; | ||
| 17 | +import org.springframework.ai.embedding.EmbeddingModel; | ||
| 18 | +import org.springframework.ai.image.ImageModel; | ||
| 19 | +import org.springframework.ai.vectorstore.SimpleVectorStore; | ||
| 20 | +import org.springframework.ai.vectorstore.VectorStore; | ||
| 21 | +import org.springframework.stereotype.Service; | ||
| 22 | +import org.springframework.validation.annotation.Validated; | ||
| 23 | + | ||
| 24 | +import java.util.List; | ||
| 25 | +import java.util.Map; | ||
| 26 | + | ||
| 27 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 28 | +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; | ||
| 29 | + | ||
| 30 | +/** | ||
| 31 | + * AI 模型 Service 实现类 | ||
| 32 | + * | ||
| 33 | + * @author fansili | ||
| 34 | + */ | ||
| 35 | +@Service | ||
| 36 | +@Validated | ||
| 37 | +public class AiModelServiceImpl implements AiModelService { | ||
| 38 | + | ||
| 39 | + @Resource | ||
| 40 | + private AiApiKeyService apiKeyService; | ||
| 41 | + | ||
| 42 | + @Resource | ||
| 43 | + private AiChatMapper modelMapper; | ||
| 44 | + | ||
| 45 | + @Resource | ||
| 46 | + private AiModelFactory modelFactory; | ||
| 47 | + | ||
| 48 | + @Override | ||
| 49 | + public Long createModel(AiModelSaveReqVO createReqVO) { | ||
| 50 | + // 1. 校验 | ||
| 51 | + AiPlatformEnum.validatePlatform(createReqVO.getPlatform()); | ||
| 52 | + apiKeyService.validateApiKey(createReqVO.getKeyId()); | ||
| 53 | + | ||
| 54 | + // 2. 插入 | ||
| 55 | + AiModelDO model = BeanUtils.toBean(createReqVO, AiModelDO.class); | ||
| 56 | + modelMapper.insert(model); | ||
| 57 | + return model.getId(); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + @Override | ||
| 61 | + public void updateModel(AiModelSaveReqVO updateReqVO) { | ||
| 62 | + // 1. 校验 | ||
| 63 | + validateModelExists(updateReqVO.getId()); | ||
| 64 | + AiPlatformEnum.validatePlatform(updateReqVO.getPlatform()); | ||
| 65 | + apiKeyService.validateApiKey(updateReqVO.getKeyId()); | ||
| 66 | + | ||
| 67 | + // 2. 更新 | ||
| 68 | + AiModelDO updateObj = BeanUtils.toBean(updateReqVO, AiModelDO.class); | ||
| 69 | + modelMapper.updateById(updateObj); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + @Override | ||
| 73 | + public void deleteModel(Long id) { | ||
| 74 | + // 校验存在 | ||
| 75 | + validateModelExists(id); | ||
| 76 | + // 删除 | ||
| 77 | + modelMapper.deleteById(id); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + private AiModelDO validateModelExists(Long id) { | ||
| 81 | + AiModelDO model = modelMapper.selectById(id); | ||
| 82 | + if (modelMapper.selectById(id) == null) { | ||
| 83 | + throw exception(MODEL_NOT_EXISTS); | ||
| 84 | + } | ||
| 85 | + return model; | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + @Override | ||
| 89 | + public AiModelDO getModel(Long id) { | ||
| 90 | + return modelMapper.selectById(id); | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + @Override | ||
| 94 | + public AiModelDO getRequiredDefaultModel(Integer type) { | ||
| 95 | + AiModelDO model = modelMapper.selectFirstByStatus(type, CommonStatusEnum.ENABLE.getStatus()); | ||
| 96 | + if (model == null) { | ||
| 97 | + throw exception(MODEL_DEFAULT_NOT_EXISTS); | ||
| 98 | + } | ||
| 99 | + return model; | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + @Override | ||
| 103 | + public PageResult<AiModelDO> getModelPage(AiModelPageReqVO pageReqVO) { | ||
| 104 | + return modelMapper.selectPage(pageReqVO); | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + @Override | ||
| 108 | + public AiModelDO validateModel(Long id) { | ||
| 109 | + AiModelDO model = validateModelExists(id); | ||
| 110 | + if (CommonStatusEnum.isDisable(model.getStatus())) { | ||
| 111 | + throw exception(MODEL_DISABLE); | ||
| 112 | + } | ||
| 113 | + return model; | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + @Override | ||
| 117 | + public List<AiModelDO> getModelListByStatusAndType(Integer status, Integer type, String platform) { | ||
| 118 | + return modelMapper.selectListByStatusAndType(status, type, platform); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + // ========== 与 Spring AI 集成 ========== | ||
| 122 | + | ||
| 123 | + @Override | ||
| 124 | + public ChatModel getChatModel(Long id) { | ||
| 125 | + AiModelDO model = validateModel(id); | ||
| 126 | + AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId()); | ||
| 127 | + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); | ||
| 128 | + return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl()); | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + @Override | ||
| 132 | + public ImageModel getImageModel(Long id) { | ||
| 133 | + AiModelDO model = validateModel(id); | ||
| 134 | + AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId()); | ||
| 135 | + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); | ||
| 136 | + return modelFactory.getOrCreateImageModel(platform, apiKey.getApiKey(), apiKey.getUrl()); | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + @Override | ||
| 140 | + public MidjourneyApi getMidjourneyApi(Long id) { | ||
| 141 | + AiModelDO model = validateModel(id); | ||
| 142 | + AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId()); | ||
| 143 | + return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl()); | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + @Override | ||
| 147 | + public SunoApi getSunoApi() { | ||
| 148 | + AiApiKeyDO apiKey = apiKeyService.getRequiredDefaultApiKey( | ||
| 149 | + AiPlatformEnum.SUNO.getPlatform(), CommonStatusEnum.ENABLE.getStatus()); | ||
| 150 | + return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl()); | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + @Override | ||
| 154 | + public VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields) { | ||
| 155 | + // 获取模型 + 密钥 | ||
| 156 | + AiModelDO model = validateModel(id); | ||
| 157 | + AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId()); | ||
| 158 | + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); | ||
| 159 | + | ||
| 160 | + // 创建或获取 EmbeddingModel 对象 | ||
| 161 | + EmbeddingModel embeddingModel = modelFactory.getOrCreateEmbeddingModel( | ||
| 162 | + platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel()); | ||
| 163 | + | ||
| 164 | + // 创建或获取 VectorStore 对象 | ||
| 165 | + return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel, metadataFields); | ||
| 166 | +// return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel, metadataFields); | ||
| 167 | +// return modelFactory.getOrCreateVectorStore(RedisVectorStore.class, embeddingModel, metadataFields); | ||
| 168 | +// return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields); | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | +} | ||
| 0 | \ No newline at end of file | 172 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolService.java
| 1 | package cn.iocoder.yudao.module.ai.service.model; | 1 | package cn.iocoder.yudao.module.ai.service.model; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO; | ||
| 5 | -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO; | ||
| 6 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | 4 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO; |
| 5 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO; | ||
| 6 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; | ||
| 7 | import jakarta.validation.Valid; | 7 | import jakarta.validation.Valid; |
| 8 | 8 | ||
| 9 | import java.util.Collection; | 9 | import java.util.Collection; |
| 10 | import java.util.List; | 10 | import java.util.List; |
| 11 | 11 | ||
| 12 | -import java.util.Set; | ||
| 13 | - | ||
| 14 | /** | 12 | /** |
| 15 | - * AI 聊天模型 Service 接口 | 13 | + * AI 工具 Service 接口 |
| 16 | * | 14 | * |
| 17 | - * @author fansili | ||
| 18 | - * @since 2024/4/24 19:42 | 15 | + * @author 芋道源码 |
| 19 | */ | 16 | */ |
| 20 | -public interface AiChatModelService { | 17 | +public interface AiToolService { |
| 21 | 18 | ||
| 22 | /** | 19 | /** |
| 23 | - * 创建聊天模型 | 20 | + * 创建工具 |
| 24 | * | 21 | * |
| 25 | * @param createReqVO 创建信息 | 22 | * @param createReqVO 创建信息 |
| 26 | * @return 编号 | 23 | * @return 编号 |
| 27 | */ | 24 | */ |
| 28 | - Long createChatModel(@Valid AiChatModelSaveReqVO createReqVO); | 25 | + Long createTool(@Valid AiToolSaveReqVO createReqVO); |
| 29 | 26 | ||
| 30 | /** | 27 | /** |
| 31 | - * 更新聊天模型 | 28 | + * 更新工具 |
| 32 | * | 29 | * |
| 33 | * @param updateReqVO 更新信息 | 30 | * @param updateReqVO 更新信息 |
| 34 | */ | 31 | */ |
| 35 | - void updateChatModel(@Valid AiChatModelSaveReqVO updateReqVO); | 32 | + void updateTool(@Valid AiToolSaveReqVO updateReqVO); |
| 36 | 33 | ||
| 37 | /** | 34 | /** |
| 38 | - * 删除聊天模型 | 35 | + * 删除工具 |
| 39 | * | 36 | * |
| 40 | * @param id 编号 | 37 | * @param id 编号 |
| 41 | */ | 38 | */ |
| 42 | - void deleteChatModel(Long id); | 39 | + void deleteTool(Long id); |
| 43 | 40 | ||
| 44 | /** | 41 | /** |
| 45 | - * 获得聊天模型 | 42 | + * 校验工具是否存在 |
| 46 | * | 43 | * |
| 47 | * @param id 编号 | 44 | * @param id 编号 |
| 48 | - * @return 聊天模型 | ||
| 49 | */ | 45 | */ |
| 50 | - AiChatModelDO getChatModel(Long id); | 46 | + void validateToolExists(Long id); |
| 51 | 47 | ||
| 52 | /** | 48 | /** |
| 53 | - * 获得默认的聊天模型 | ||
| 54 | - * | ||
| 55 | - * 如果获取不到,则抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 业务异常 | 49 | + * 获得工具 |
| 56 | * | 50 | * |
| 57 | - * @return 聊天模型 | 51 | + * @param id 编号 |
| 52 | + * @return 工具 | ||
| 58 | */ | 53 | */ |
| 59 | - AiChatModelDO getRequiredDefaultChatModel(); | 54 | + AiToolDO getTool(Long id); |
| 60 | 55 | ||
| 61 | /** | 56 | /** |
| 62 | - * 获得聊天模型分页 | 57 | + * 获得工具列表 |
| 63 | * | 58 | * |
| 64 | - * @param pageReqVO 分页查询 | ||
| 65 | - * @return 聊天模型分页 | 59 | + * @param ids 编号列表 |
| 60 | + * @return 工具列表 | ||
| 66 | */ | 61 | */ |
| 67 | - PageResult<AiChatModelDO> getChatModelPage(AiChatModelPageReqVO pageReqVO); | 62 | + List<AiToolDO> getToolList(Collection<Long> ids); |
| 68 | 63 | ||
| 69 | /** | 64 | /** |
| 70 | - * 校验聊天模型 | 65 | + * 获得工具分页 |
| 71 | * | 66 | * |
| 72 | - * @param id 编号 | ||
| 73 | - * @return 聊天模型 | 67 | + * @param pageReqVO 分页查询 |
| 68 | + * @return 工具分页 | ||
| 74 | */ | 69 | */ |
| 75 | - AiChatModelDO validateChatModel(Long id); | 70 | + PageResult<AiToolDO> getToolPage(AiToolPageReqVO pageReqVO); |
| 76 | 71 | ||
| 77 | /** | 72 | /** |
| 78 | - * 获得聊天模型列表 | 73 | + * 获得工具列表 |
| 79 | * | 74 | * |
| 80 | * @param status 状态 | 75 | * @param status 状态 |
| 81 | - * @return 聊天模型列表 | 76 | + * @return 工具列表 |
| 82 | */ | 77 | */ |
| 83 | - List<AiChatModelDO> getChatModelListByStatus(Integer status); | 78 | + List<AiToolDO> getToolListByStatus(Integer status); |
| 84 | 79 | ||
| 85 | - /** | ||
| 86 | - * 获得聊天模型列表 | ||
| 87 | - * | ||
| 88 | - * @param ids 编号数组 | ||
| 89 | - * @return 模型列表 | ||
| 90 | - */ | ||
| 91 | - List<AiChatModelDO> getChatModelList(Collection<Long> ids); | ||
| 92 | -} | 80 | +} |
| 93 | \ No newline at end of file | 81 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.service.model; | ||
| 2 | + | ||
| 3 | +import cn.hutool.extra.spring.SpringUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 5 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 6 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO; | ||
| 8 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; | ||
| 9 | +import cn.iocoder.yudao.module.ai.dal.mysql.model.AiToolMapper; | ||
| 10 | +import jakarta.annotation.Resource; | ||
| 11 | +import org.springframework.beans.factory.NoSuchBeanDefinitionException; | ||
| 12 | +import org.springframework.stereotype.Service; | ||
| 13 | +import org.springframework.validation.annotation.Validated; | ||
| 14 | + | ||
| 15 | +import java.util.Collection; | ||
| 16 | +import java.util.List; | ||
| 17 | + | ||
| 18 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 19 | +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.TOOL_NAME_NOT_EXISTS; | ||
| 20 | +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.TOOL_NOT_EXISTS; | ||
| 21 | + | ||
| 22 | +/** | ||
| 23 | + * AI 工具 Service 实现类 | ||
| 24 | + * | ||
| 25 | + * @author 芋道源码 | ||
| 26 | + */ | ||
| 27 | +@Service | ||
| 28 | +@Validated | ||
| 29 | +public class AiToolServiceImpl implements AiToolService { | ||
| 30 | + | ||
| 31 | + @Resource | ||
| 32 | + private AiToolMapper toolMapper; | ||
| 33 | + | ||
| 34 | + @Override | ||
| 35 | + public Long createTool(AiToolSaveReqVO createReqVO) { | ||
| 36 | + // 校验名称是否存在 | ||
| 37 | + validateToolNameExists(createReqVO.getName()); | ||
| 38 | + | ||
| 39 | + // 插入 | ||
| 40 | + AiToolDO tool = BeanUtils.toBean(createReqVO, AiToolDO.class); | ||
| 41 | + toolMapper.insert(tool); | ||
| 42 | + return tool.getId(); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + @Override | ||
| 46 | + public void updateTool(AiToolSaveReqVO updateReqVO) { | ||
| 47 | + // 1.1 校验存在 | ||
| 48 | + validateToolExists(updateReqVO.getId()); | ||
| 49 | + // 1.2 校验名称是否存在 | ||
| 50 | + validateToolNameExists(updateReqVO.getName()); | ||
| 51 | + | ||
| 52 | + // 2. 更新 | ||
| 53 | + AiToolDO updateObj = BeanUtils.toBean(updateReqVO, AiToolDO.class); | ||
| 54 | + toolMapper.updateById(updateObj); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + @Override | ||
| 58 | + public void deleteTool(Long id) { | ||
| 59 | + // 校验存在 | ||
| 60 | + validateToolExists(id); | ||
| 61 | + // 删除 | ||
| 62 | + toolMapper.deleteById(id); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + @Override | ||
| 66 | + public void validateToolExists(Long id) { | ||
| 67 | + if (toolMapper.selectById(id) == null) { | ||
| 68 | + throw exception(TOOL_NOT_EXISTS); | ||
| 69 | + } | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + private void validateToolNameExists(String name) { | ||
| 73 | + try { | ||
| 74 | + SpringUtil.getBean(name); | ||
| 75 | + } catch (NoSuchBeanDefinitionException e) { | ||
| 76 | + throw exception(TOOL_NAME_NOT_EXISTS, name); | ||
| 77 | + } | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + @Override | ||
| 81 | + public AiToolDO getTool(Long id) { | ||
| 82 | + return toolMapper.selectById(id); | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + @Override | ||
| 86 | + public List<AiToolDO> getToolList(Collection<Long> ids) { | ||
| 87 | + return toolMapper.selectBatchIds(ids); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + @Override | ||
| 91 | + public PageResult<AiToolDO> getToolPage(AiToolPageReqVO pageReqVO) { | ||
| 92 | + return toolMapper.selectPage(pageReqVO); | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + @Override | ||
| 96 | + public List<AiToolDO> getToolListByStatus(Integer status) { | ||
| 97 | + return toolMapper.selectListByStatus(status); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | +} | ||
| 0 | \ No newline at end of file | 101 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/DirectoryListToolFunction.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.service.model.tool; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.date.LocalDateTimeUtil; | ||
| 4 | +import cn.hutool.core.io.FileUtil; | ||
| 5 | +import cn.hutool.core.util.ArrayUtil; | ||
| 6 | +import cn.hutool.core.util.StrUtil; | ||
| 7 | +import com.fasterxml.jackson.annotation.JsonClassDescription; | ||
| 8 | +import com.fasterxml.jackson.annotation.JsonProperty; | ||
| 9 | +import com.fasterxml.jackson.annotation.JsonPropertyDescription; | ||
| 10 | +import lombok.AllArgsConstructor; | ||
| 11 | +import lombok.Data; | ||
| 12 | +import lombok.NoArgsConstructor; | ||
| 13 | +import org.springframework.stereotype.Component; | ||
| 14 | + | ||
| 15 | +import java.io.File; | ||
| 16 | +import java.util.Arrays; | ||
| 17 | +import java.util.Collections; | ||
| 18 | +import java.util.List; | ||
| 19 | +import java.util.function.Function; | ||
| 20 | + | ||
| 21 | +import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN; | ||
| 22 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||
| 23 | + | ||
| 24 | +/** | ||
| 25 | + * 工具:列出指定目录的文件列表 | ||
| 26 | + * | ||
| 27 | + * @author 芋道源码 | ||
| 28 | + */ | ||
| 29 | +@Component("directory_list") | ||
| 30 | +public class DirectoryListToolFunction implements Function<DirectoryListToolFunction.Request, DirectoryListToolFunction.Response> { | ||
| 31 | + | ||
| 32 | + @Data | ||
| 33 | + @JsonClassDescription("列出指定目录的文件列表") | ||
| 34 | + public static class Request { | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * 目录路径 | ||
| 38 | + */ | ||
| 39 | + @JsonProperty(required = true, value = "path") | ||
| 40 | + @JsonPropertyDescription("目录路径,例如说:/Users/yunai") | ||
| 41 | + private String path; | ||
| 42 | + | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + @Data | ||
| 46 | + @AllArgsConstructor | ||
| 47 | + @NoArgsConstructor | ||
| 48 | + public static class Response { | ||
| 49 | + | ||
| 50 | + /** | ||
| 51 | + * 文件列表 | ||
| 52 | + */ | ||
| 53 | + private List<File> files; | ||
| 54 | + | ||
| 55 | + @Data | ||
| 56 | + public static class File { | ||
| 57 | + | ||
| 58 | + /** | ||
| 59 | + * 是否为目录 | ||
| 60 | + */ | ||
| 61 | + private Boolean directory; | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 名称 | ||
| 65 | + */ | ||
| 66 | + private String name; | ||
| 67 | + | ||
| 68 | + /** | ||
| 69 | + * 大小,仅对文件有效 | ||
| 70 | + */ | ||
| 71 | + private String size; | ||
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * 最后修改时间 | ||
| 75 | + */ | ||
| 76 | + private String lastModified; | ||
| 77 | + | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + @Override | ||
| 83 | + public Response apply(Request request) { | ||
| 84 | + // 校验目录存在 | ||
| 85 | + String path = StrUtil.blankToDefault(request.getPath(), "/"); | ||
| 86 | + if (!FileUtil.exist(path) || !FileUtil.isDirectory(path)) { | ||
| 87 | + return new Response(Collections.emptyList()); | ||
| 88 | + } | ||
| 89 | + // 列出目录 | ||
| 90 | + File[] files = FileUtil.ls(path); | ||
| 91 | + if (ArrayUtil.isEmpty(files)) { | ||
| 92 | + return new Response(Collections.emptyList()); | ||
| 93 | + } | ||
| 94 | + return new Response(convertList(Arrays.asList(files), file -> | ||
| 95 | + new Response.File().setDirectory(file.isDirectory()).setName(file.getName()) | ||
| 96 | + .setLastModified(LocalDateTimeUtil.format(LocalDateTimeUtil.of(file.lastModified()), NORM_DATETIME_PATTERN)))); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | +} | ||
| 0 | \ No newline at end of file | 100 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/WeatherQueryToolFunction.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.service.model.tool; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.date.LocalDateTimeUtil; | ||
| 4 | +import cn.hutool.core.util.RandomUtil; | ||
| 5 | +import cn.hutool.core.util.StrUtil; | ||
| 6 | +import com.fasterxml.jackson.annotation.JsonClassDescription; | ||
| 7 | +import com.fasterxml.jackson.annotation.JsonProperty; | ||
| 8 | +import com.fasterxml.jackson.annotation.JsonPropertyDescription; | ||
| 9 | +import lombok.AllArgsConstructor; | ||
| 10 | +import lombok.Data; | ||
| 11 | +import lombok.NoArgsConstructor; | ||
| 12 | +import org.springframework.stereotype.Component; | ||
| 13 | + | ||
| 14 | +import java.time.LocalDateTime; | ||
| 15 | +import java.util.function.Function; | ||
| 16 | + | ||
| 17 | +import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN; | ||
| 18 | + | ||
| 19 | +/** | ||
| 20 | + * 工具:查询指定城市的天气信息 | ||
| 21 | + * | ||
| 22 | + * @author 芋道源码 | ||
| 23 | + */ | ||
| 24 | +@Component("weather_query") | ||
| 25 | +public class WeatherQueryToolFunction | ||
| 26 | + implements Function<WeatherQueryToolFunction.Request, WeatherQueryToolFunction.Response> { | ||
| 27 | + | ||
| 28 | + private static final String[] WEATHER_CONDITIONS = { "晴朗", "多云", "阴天", "小雨", "大雨", "雷雨", "小雪", "大雪" }; | ||
| 29 | + | ||
| 30 | + @Data | ||
| 31 | + @JsonClassDescription("查询指定城市的天气信息") | ||
| 32 | + public static class Request { | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 城市名称 | ||
| 36 | + */ | ||
| 37 | + @JsonProperty(required = true, value = "city") | ||
| 38 | + @JsonPropertyDescription("城市名称,例如:北京、上海、广州") | ||
| 39 | + private String city; | ||
| 40 | + | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + @Data | ||
| 44 | + @AllArgsConstructor | ||
| 45 | + @NoArgsConstructor | ||
| 46 | + public static class Response { | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * 城市名称 | ||
| 50 | + */ | ||
| 51 | + private String city; | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 天气信息 | ||
| 55 | + */ | ||
| 56 | + private WeatherInfo weatherInfo; | ||
| 57 | + | ||
| 58 | + @Data | ||
| 59 | + @AllArgsConstructor | ||
| 60 | + @NoArgsConstructor | ||
| 61 | + public static class WeatherInfo { | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 温度(摄氏度) | ||
| 65 | + */ | ||
| 66 | + private Integer temperature; | ||
| 67 | + | ||
| 68 | + /** | ||
| 69 | + * 天气状况 | ||
| 70 | + */ | ||
| 71 | + private String condition; | ||
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * 湿度百分比 | ||
| 75 | + */ | ||
| 76 | + private Integer humidity; | ||
| 77 | + | ||
| 78 | + /** | ||
| 79 | + * 风速(km/h) | ||
| 80 | + */ | ||
| 81 | + private Integer windSpeed; | ||
| 82 | + | ||
| 83 | + /** | ||
| 84 | + * 查询时间 | ||
| 85 | + */ | ||
| 86 | + private String queryTime; | ||
| 87 | + | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + @Override | ||
| 93 | + public Response apply(Request request) { | ||
| 94 | + // 检查城市名称是否为空 | ||
| 95 | + if (StrUtil.isBlank(request.getCity())) { | ||
| 96 | + return new Response("未知城市", null); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + // 获取天气数据 | ||
| 100 | + String city = request.getCity(); | ||
| 101 | + Response.WeatherInfo weatherInfo = generateMockWeatherInfo(); | ||
| 102 | + return new Response(city, weatherInfo); | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + /** | ||
| 106 | + * 生成模拟的天气数据 | ||
| 107 | + * 在实际应用中,应替换为真实 API 调用 | ||
| 108 | + */ | ||
| 109 | + private Response.WeatherInfo generateMockWeatherInfo() { | ||
| 110 | + int temperature = RandomUtil.randomInt(-5, 30); | ||
| 111 | + int humidity = RandomUtil.randomInt(1, 100); | ||
| 112 | + int windSpeed = RandomUtil.randomInt(1, 30); | ||
| 113 | + String condition = RandomUtil.randomEle(WEATHER_CONDITIONS); | ||
| 114 | + return new Response.WeatherInfo(temperature, condition, humidity, windSpeed, | ||
| 115 | + LocalDateTimeUtil.format(LocalDateTime.now(), NORM_DATETIME_PATTERN)); | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | +} | ||
| 0 | \ No newline at end of file | 119 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java
| @@ -16,7 +16,7 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO; | @@ -16,7 +16,7 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO; | ||
| 16 | import cn.iocoder.yudao.module.ai.dal.mysql.music.AiMusicMapper; | 16 | import cn.iocoder.yudao.module.ai.dal.mysql.music.AiMusicMapper; |
| 17 | import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum; | 17 | import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum; |
| 18 | import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum; | 18 | import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum; |
| 19 | -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; | 19 | +import cn.iocoder.yudao.module.ai.service.model.AiModelService; |
| 20 | import cn.iocoder.yudao.module.infra.api.file.FileApi; | 20 | import cn.iocoder.yudao.module.infra.api.file.FileApi; |
| 21 | import jakarta.annotation.Resource; | 21 | import jakarta.annotation.Resource; |
| 22 | import lombok.extern.slf4j.Slf4j; | 22 | import lombok.extern.slf4j.Slf4j; |
| @@ -41,7 +41,7 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MUSIC_NOT_EXIS | @@ -41,7 +41,7 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MUSIC_NOT_EXIS | ||
| 41 | public class AiMusicServiceImpl implements AiMusicService { | 41 | public class AiMusicServiceImpl implements AiMusicService { |
| 42 | 42 | ||
| 43 | @Resource | 43 | @Resource |
| 44 | - private AiApiKeyService apiKeyService; | 44 | + private AiModelService modelService; |
| 45 | 45 | ||
| 46 | @Resource | 46 | @Resource |
| 47 | private AiMusicMapper musicMapper; | 47 | private AiMusicMapper musicMapper; |
| @@ -53,7 +53,7 @@ public class AiMusicServiceImpl implements AiMusicService { | @@ -53,7 +53,7 @@ public class AiMusicServiceImpl implements AiMusicService { | ||
| 53 | @Transactional(rollbackFor = Exception.class) | 53 | @Transactional(rollbackFor = Exception.class) |
| 54 | public List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO) { | 54 | public List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO) { |
| 55 | // 1. 调用 Suno 生成音乐 | 55 | // 1. 调用 Suno 生成音乐 |
| 56 | - SunoApi sunoApi = apiKeyService.getSunoApi(); | 56 | + SunoApi sunoApi = modelService.getSunoApi(); |
| 57 | List<SunoApi.MusicData> musicDataList; | 57 | List<SunoApi.MusicData> musicDataList; |
| 58 | if (Objects.equals(AiMusicGenerateModeEnum.DESCRIPTION.getMode(), reqVO.getGenerateMode())) { | 58 | if (Objects.equals(AiMusicGenerateModeEnum.DESCRIPTION.getMode(), reqVO.getGenerateMode())) { |
| 59 | // 1.1 描述模式 | 59 | // 1.1 描述模式 |
| @@ -88,7 +88,7 @@ public class AiMusicServiceImpl implements AiMusicService { | @@ -88,7 +88,7 @@ public class AiMusicServiceImpl implements AiMusicService { | ||
| 88 | log.info("[syncMusic][Suno 开始同步, 共 ({}) 个任务]", streamingTask.size()); | 88 | log.info("[syncMusic][Suno 开始同步, 共 ({}) 个任务]", streamingTask.size()); |
| 89 | 89 | ||
| 90 | // GET 请求,为避免参数过长,分批次处理 | 90 | // GET 请求,为避免参数过长,分批次处理 |
| 91 | - SunoApi sunoApi = apiKeyService.getSunoApi(); | 91 | + SunoApi sunoApi = modelService.getSunoApi(); |
| 92 | CollUtil.split(streamingTask, 36).forEach(chunkList -> { | 92 | CollUtil.split(streamingTask, 36).forEach(chunkList -> { |
| 93 | Map<String, Long> taskIdMap = convertMap(chunkList, AiMusicDO::getTaskId, AiMusicDO::getId); | 93 | Map<String, Long> taskIdMap = convertMap(chunkList, AiMusicDO::getTaskId, AiMusicDO::getId); |
| 94 | List<SunoApi.MusicData> musicTaskList = sunoApi.getMusicList(new ArrayList<>(taskIdMap.keySet())); | 94 | List<SunoApi.MusicData> musicTaskList = sunoApi.getMusicList(new ArrayList<>(taskIdMap.keySet())); |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java
| 1 | package cn.iocoder.yudao.module.ai.service.write; | 1 | package cn.iocoder.yudao.module.ai.service.write; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | -import cn.hutool.core.lang.Assert; | 4 | +import cn.hutool.core.util.ObjUtil; |
| 5 | import cn.hutool.core.util.StrUtil; | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum; | ||
| 6 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | 7 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; |
| 7 | import cn.iocoder.yudao.framework.ai.core.util.AiUtils; | 8 | import cn.iocoder.yudao.framework.ai.core.util.AiUtils; |
| 8 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 9 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| @@ -11,17 +12,16 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | @@ -11,17 +12,16 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 11 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | 12 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; |
| 12 | import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO; | 13 | import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO; |
| 13 | import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWritePageReqVO; | 14 | import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWritePageReqVO; |
| 14 | -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | ||
| 15 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | 15 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; |
| 16 | +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; | ||
| 16 | import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO; | 17 | import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO; |
| 17 | import cn.iocoder.yudao.module.ai.dal.mysql.write.AiWriteMapper; | 18 | import cn.iocoder.yudao.module.ai.dal.mysql.write.AiWriteMapper; |
| 18 | import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum; | 19 | import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum; |
| 19 | import cn.iocoder.yudao.module.ai.enums.DictTypeConstants; | 20 | import cn.iocoder.yudao.module.ai.enums.DictTypeConstants; |
| 20 | import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants; | 21 | import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants; |
| 21 | import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum; | 22 | import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum; |
| 22 | -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; | ||
| 23 | -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; | ||
| 24 | import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; | 23 | import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; |
| 24 | +import cn.iocoder.yudao.module.ai.service.model.AiModelService; | ||
| 25 | import cn.iocoder.yudao.module.system.api.dict.DictDataApi; | 25 | import cn.iocoder.yudao.module.system.api.dict.DictDataApi; |
| 26 | import jakarta.annotation.Resource; | 26 | import jakarta.annotation.Resource; |
| 27 | import lombok.extern.slf4j.Slf4j; | 27 | import lombok.extern.slf4j.Slf4j; |
| @@ -42,7 +42,7 @@ import java.util.Objects; | @@ -42,7 +42,7 @@ import java.util.Objects; | ||
| 42 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 42 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 43 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; | 43 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; |
| 44 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 44 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 45 | -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WRITE_NOT_EXISTS; | 45 | +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; |
| 46 | 46 | ||
| 47 | /** | 47 | /** |
| 48 | * AI 写作 Service 实现类 | 48 | * AI 写作 Service 实现类 |
| @@ -54,17 +54,15 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WRITE_NOT_EXIS | @@ -54,17 +54,15 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WRITE_NOT_EXIS | ||
| 54 | public class AiWriteServiceImpl implements AiWriteService { | 54 | public class AiWriteServiceImpl implements AiWriteService { |
| 55 | 55 | ||
| 56 | @Resource | 56 | @Resource |
| 57 | - private AiApiKeyService apiKeyService; | ||
| 58 | - @Resource | ||
| 59 | - private AiChatModelService chatModalService; | 57 | + private AiModelService modalService; |
| 60 | @Resource | 58 | @Resource |
| 61 | private AiChatRoleService chatRoleService; | 59 | private AiChatRoleService chatRoleService; |
| 62 | 60 | ||
| 63 | @Resource | 61 | @Resource |
| 64 | - private DictDataApi dictDataApi; | 62 | + private AiWriteMapper writeMapper; |
| 65 | 63 | ||
| 66 | @Resource | 64 | @Resource |
| 67 | - private AiWriteMapper writeMapper; | 65 | + private DictDataApi dictDataApi; |
| 68 | 66 | ||
| 69 | @Override | 67 | @Override |
| 70 | public Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId) { | 68 | public Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId) { |
| @@ -72,17 +70,17 @@ public class AiWriteServiceImpl implements AiWriteService { | @@ -72,17 +70,17 @@ public class AiWriteServiceImpl implements AiWriteService { | ||
| 72 | AiChatRoleDO writeRole = CollUtil.getFirst( | 70 | AiChatRoleDO writeRole = CollUtil.getFirst( |
| 73 | chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_WRITE_ROLE.getName())); | 71 | chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_WRITE_ROLE.getName())); |
| 74 | // 1.1 获取写作执行模型 | 72 | // 1.1 获取写作执行模型 |
| 75 | - AiChatModelDO model = getModel(writeRole); | 73 | + AiModelDO model = getModel(writeRole); |
| 76 | // 1.2 获取角色设定消息 | 74 | // 1.2 获取角色设定消息 |
| 77 | String systemMessage = Objects.nonNull(writeRole) && StrUtil.isNotBlank(writeRole.getSystemMessage()) | 75 | String systemMessage = Objects.nonNull(writeRole) && StrUtil.isNotBlank(writeRole.getSystemMessage()) |
| 78 | ? writeRole.getSystemMessage() : AiChatRoleEnum.AI_WRITE_ROLE.getSystemMessage(); | 76 | ? writeRole.getSystemMessage() : AiChatRoleEnum.AI_WRITE_ROLE.getSystemMessage(); |
| 79 | // 1.3 校验平台 | 77 | // 1.3 校验平台 |
| 80 | AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); | 78 | AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); |
| 81 | - StreamingChatModel chatModel = apiKeyService.getChatModel(model.getKeyId()); | 79 | + StreamingChatModel chatModel = modalService.getChatModel(model.getKeyId()); |
| 82 | 80 | ||
| 83 | // 2. 插入写作信息 | 81 | // 2. 插入写作信息 |
| 84 | - AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class, | ||
| 85 | - write -> write.setUserId(userId).setPlatform(platform.getPlatform()).setModel(model.getModel())); | 82 | + AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class, write -> write.setUserId(userId) |
| 83 | + .setPlatform(platform.getPlatform()).setModelId(model.getId()).setModel(model.getModel())); | ||
| 86 | writeMapper.insert(writeDO); | 84 | writeMapper.insert(writeDO); |
| 87 | 85 | ||
| 88 | // 3.1 构建 Prompt,并进行调用 | 86 | // 3.1 构建 Prompt,并进行调用 |
| @@ -92,7 +90,7 @@ public class AiWriteServiceImpl implements AiWriteService { | @@ -92,7 +90,7 @@ public class AiWriteServiceImpl implements AiWriteService { | ||
| 92 | // 3.2 流式返回 | 90 | // 3.2 流式返回 |
| 93 | StringBuffer contentBuffer = new StringBuffer(); | 91 | StringBuffer contentBuffer = new StringBuffer(); |
| 94 | return streamResponse.map(chunk -> { | 92 | return streamResponse.map(chunk -> { |
| 95 | - String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getContent() : null; | 93 | + String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null; |
| 96 | newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况 | 94 | newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况 |
| 97 | contentBuffer.append(newContent); | 95 | contentBuffer.append(newContent); |
| 98 | // 响应结果 | 96 | // 响应结果 |
| @@ -109,19 +107,25 @@ public class AiWriteServiceImpl implements AiWriteService { | @@ -109,19 +107,25 @@ public class AiWriteServiceImpl implements AiWriteService { | ||
| 109 | }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR))); | 107 | }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR))); |
| 110 | } | 108 | } |
| 111 | 109 | ||
| 112 | - private AiChatModelDO getModel(AiChatRoleDO writeRole) { | ||
| 113 | - AiChatModelDO model = null; | 110 | + private AiModelDO getModel(AiChatRoleDO writeRole) { |
| 111 | + AiModelDO model = null; | ||
| 114 | if (Objects.nonNull(writeRole) && Objects.nonNull(writeRole.getModelId())) { | 112 | if (Objects.nonNull(writeRole) && Objects.nonNull(writeRole.getModelId())) { |
| 115 | - model = chatModalService.getChatModel(writeRole.getModelId()); | 113 | + model = modalService.getModel(writeRole.getModelId()); |
| 114 | + } | ||
| 115 | + if (model == null) { | ||
| 116 | + model = modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType()); | ||
| 116 | } | 117 | } |
| 118 | + // 校验模型存在、且合法 | ||
| 117 | if (model == null) { | 119 | if (model == null) { |
| 118 | - model = chatModalService.getRequiredDefaultChatModel(); | 120 | + throw exception(MODEL_NOT_EXISTS); |
| 121 | + } | ||
| 122 | + if (ObjUtil.notEqual(model.getType(), AiModelTypeEnum.CHAT.getType())) { | ||
| 123 | + throw exception(MODEL_USE_TYPE_ERROR); | ||
| 119 | } | 124 | } |
| 120 | - Assert.notNull(model, "[AI] 获取不到模型"); | ||
| 121 | return model; | 125 | return model; |
| 122 | } | 126 | } |
| 123 | 127 | ||
| 124 | - private Prompt buildPrompt(AiWriteGenerateReqVO generateReqVO, AiChatModelDO model, String systemMessage) { | 128 | + private Prompt buildPrompt(AiWriteGenerateReqVO generateReqVO, AiModelDO model, String systemMessage) { |
| 125 | // 1. 构建 message 列表 | 129 | // 1. 构建 message 列表 |
| 126 | List<Message> chatMessages = buildMessages(generateReqVO, systemMessage); | 130 | List<Message> chatMessages = buildMessages(generateReqVO, systemMessage); |
| 127 | // 2. 构建 options 对象 | 131 | // 2. 构建 options 对象 |
yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
| @@ -14,93 +14,105 @@ | @@ -14,93 +14,105 @@ | ||
| 14 | <name>${project.artifactId}</name> | 14 | <name>${project.artifactId}</name> |
| 15 | <description>AI 大模型拓展,接入国内外大模型</description> | 15 | <description>AI 大模型拓展,接入国内外大模型</description> |
| 16 | <properties> | 16 | <properties> |
| 17 | - <spring-ai.groupId>group.springframework.ai</spring-ai.groupId> | ||
| 18 | - <spring-ai.version>1.1.0</spring-ai.version> | 17 | + <spring-ai.version>1.0.0-M6</spring-ai.version> |
| 19 | </properties> | 18 | </properties> |
| 20 | 19 | ||
| 21 | <dependencies> | 20 | <dependencies> |
| 22 | <dependency> | 21 | <dependency> |
| 23 | - <groupId>${spring-ai.groupId}</groupId> | ||
| 24 | - <artifactId>spring-ai-zhipuai-spring-boot-starter</artifactId> | ||
| 25 | - <version>${spring-ai.version}</version> | 22 | + <groupId>cn.iocoder.boot</groupId> |
| 23 | + <artifactId>yudao-common</artifactId> | ||
| 26 | </dependency> | 24 | </dependency> |
| 25 | + | ||
| 26 | + <!-- Spring AI Model 模型接入 --> | ||
| 27 | <dependency> | 27 | <dependency> |
| 28 | - <groupId>${spring-ai.groupId}</groupId> | 28 | + <groupId>org.springframework.ai</groupId> |
| 29 | <artifactId>spring-ai-openai-spring-boot-starter</artifactId> | 29 | <artifactId>spring-ai-openai-spring-boot-starter</artifactId> |
| 30 | <version>${spring-ai.version}</version> | 30 | <version>${spring-ai.version}</version> |
| 31 | </dependency> | 31 | </dependency> |
| 32 | <dependency> | 32 | <dependency> |
| 33 | - <groupId>${spring-ai.groupId}</groupId> | 33 | + <groupId>org.springframework.ai</groupId> |
| 34 | <artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId> | 34 | <artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId> |
| 35 | <version>${spring-ai.version}</version> | 35 | <version>${spring-ai.version}</version> |
| 36 | </dependency> | 36 | </dependency> |
| 37 | <dependency> | 37 | <dependency> |
| 38 | - <groupId>${spring-ai.groupId}</groupId> | 38 | + <groupId>org.springframework.ai</groupId> |
| 39 | <artifactId>spring-ai-ollama-spring-boot-starter</artifactId> | 39 | <artifactId>spring-ai-ollama-spring-boot-starter</artifactId> |
| 40 | <version>${spring-ai.version}</version> | 40 | <version>${spring-ai.version}</version> |
| 41 | </dependency> | 41 | </dependency> |
| 42 | <dependency> | 42 | <dependency> |
| 43 | - <groupId>${spring-ai.groupId}</groupId> | 43 | + <groupId>org.springframework.ai</groupId> |
| 44 | <artifactId>spring-ai-stability-ai-spring-boot-starter</artifactId> | 44 | <artifactId>spring-ai-stability-ai-spring-boot-starter</artifactId> |
| 45 | <version>${spring-ai.version}</version> | 45 | <version>${spring-ai.version}</version> |
| 46 | </dependency> | 46 | </dependency> |
| 47 | - | ||
| 48 | - <!-- 向量化,基于 Redis 存储,Tika 解析内容 --> | ||
| 49 | - | ||
| 50 | - <!-- 暂不做经济型,先注释 TODO 经济型是啥呀? --> | ||
| 51 | - <!-- <dependency>--> | ||
| 52 | - <!-- <groupId>${spring-ai.groupId}</groupId>--> | ||
| 53 | - <!-- <artifactId>spring-ai-transformers-spring-boot-starter</artifactId>--> | ||
| 54 | - <!-- <version>${spring-ai.version}</version>--> | ||
| 55 | - <!-- </dependency>--> | ||
| 56 | - <dependency> | ||
| 57 | - <groupId>${spring-ai.groupId}</groupId> | ||
| 58 | - <artifactId>spring-ai-tika-document-reader</artifactId> | 47 | + <dependency> |
| 48 | + <!-- 通义千问 --> | ||
| 49 | + <groupId>com.alibaba.cloud.ai</groupId> | ||
| 50 | + <artifactId>spring-ai-alibaba-starter</artifactId> | ||
| 51 | + <version>${spring-ai.version}.1</version> | ||
| 52 | + </dependency> | ||
| 53 | + <dependency> | ||
| 54 | + <!-- 文心一言 --> | ||
| 55 | + <groupId>org.springframework.ai</groupId> | ||
| 56 | + <artifactId>spring-ai-qianfan-spring-boot-starter</artifactId> | ||
| 59 | <version>${spring-ai.version}</version> | 57 | <version>${spring-ai.version}</version> |
| 60 | - <!-- TODO 芋艿:boot 项目里,不引入 cloud 依赖!!!另外,这样也是为了解决启动报错的问题! --> | ||
| 61 | - <exclusions> | ||
| 62 | - <exclusion> | ||
| 63 | - <artifactId>spring-cloud-function-context</artifactId> | ||
| 64 | - <groupId>org.springframework.cloud</groupId> | ||
| 65 | - </exclusion> | ||
| 66 | - <exclusion> | ||
| 67 | - <artifactId>spring-cloud-function-core</artifactId> | ||
| 68 | - <groupId>org.springframework.cloud</groupId> | ||
| 69 | - </exclusion> | ||
| 70 | - </exclusions> | ||
| 71 | </dependency> | 58 | </dependency> |
| 72 | <dependency> | 59 | <dependency> |
| 73 | - <groupId>${spring-ai.groupId}</groupId> | ||
| 74 | - <artifactId>spring-ai-redis-store</artifactId> | 60 | + <!-- 智谱 GLM --> |
| 61 | + <groupId>org.springframework.ai</groupId> | ||
| 62 | + <artifactId>spring-ai-zhipuai-spring-boot-starter</artifactId> | ||
| 63 | + <version>${spring-ai.version}</version> | ||
| 64 | + </dependency> | ||
| 65 | + <dependency> | ||
| 66 | + <groupId>org.springframework.ai</groupId> | ||
| 67 | + <artifactId>spring-ai-minimax-spring-boot-starter</artifactId> | ||
| 68 | + <version>${spring-ai.version}</version> | ||
| 69 | + </dependency> | ||
| 70 | + <dependency> | ||
| 71 | + <groupId>org.springframework.ai</groupId> | ||
| 72 | + <artifactId>spring-ai-moonshot-spring-boot-starter</artifactId> | ||
| 75 | <version>${spring-ai.version}</version> | 73 | <version>${spring-ai.version}</version> |
| 76 | </dependency> | 74 | </dependency> |
| 77 | 75 | ||
| 76 | + <!-- 向量存储:https://db-engines.com/en/ranking/vector+dbms --> | ||
| 78 | <dependency> | 77 | <dependency> |
| 79 | - <groupId>cn.iocoder.boot</groupId> | ||
| 80 | - <artifactId>yudao-spring-boot-starter-redis</artifactId> | 78 | + <!-- Qdrant:https://qdrant.tech/ --> |
| 79 | + <groupId>org.springframework.ai</groupId> | ||
| 80 | + <artifactId>spring-ai-qdrant-store</artifactId> | ||
| 81 | + <version>${spring-ai.version}</version> | ||
| 81 | </dependency> | 82 | </dependency> |
| 82 | 83 | ||
| 83 | <dependency> | 84 | <dependency> |
| 85 | + <!-- Redis:https://redis.io/docs/latest/develop/get-started/vector-database/ --> | ||
| 86 | + <groupId>org.springframework.ai</groupId> | ||
| 87 | + <artifactId>spring-ai-redis-store</artifactId> | ||
| 88 | + <version>${spring-ai.version}</version> | ||
| 89 | + </dependency> | ||
| 90 | + <dependency> | ||
| 84 | <groupId>cn.iocoder.boot</groupId> | 91 | <groupId>cn.iocoder.boot</groupId> |
| 85 | - <artifactId>yudao-common</artifactId> | 92 | + <artifactId>yudao-spring-boot-starter-redis</artifactId> |
| 86 | </dependency> | 93 | </dependency> |
| 87 | 94 | ||
| 88 | <dependency> | 95 | <dependency> |
| 89 | - <groupId>${spring-ai.groupId}</groupId> | ||
| 90 | - <artifactId>spring-ai-qianfan-spring-boot-starter</artifactId> | 96 | + <!-- Milvus:https://milvus.io/ --> |
| 97 | + <groupId>org.springframework.ai</groupId> | ||
| 98 | + <artifactId>spring-ai-milvus-store</artifactId> | ||
| 91 | <version>${spring-ai.version}</version> | 99 | <version>${spring-ai.version}</version> |
| 92 | </dependency> | 100 | </dependency> |
| 93 | 101 | ||
| 94 | - <!-- 阿里云 通义千问 --> | ||
| 95 | - <!-- TODO 芋艿:等 spring cloud alibaba ai 发布最新的时候,可以替换掉这个依赖,并且删除我们直接 cv 的代码 --> | ||
| 96 | <dependency> | 102 | <dependency> |
| 97 | - <groupId>com.alibaba</groupId> | ||
| 98 | - <artifactId>dashscope-sdk-java</artifactId> | ||
| 99 | - <version>2.14.0</version> | 103 | + <!-- Tika:负责内容的解析 --> |
| 104 | + <groupId>org.springframework.ai</groupId> | ||
| 105 | + <artifactId>spring-ai-tika-document-reader</artifactId> | ||
| 106 | + <version>${spring-ai.version}</version> | ||
| 107 | + <!-- TODO 芋艿:boot 项目里,不引入 cloud 依赖!!!另外,这样也是为了解决启动报错的问题! --> | ||
| 100 | <exclusions> | 108 | <exclusions> |
| 101 | <exclusion> | 109 | <exclusion> |
| 102 | - <groupId>org.slf4j</groupId> | ||
| 103 | - <artifactId>slf4j-simple</artifactId> | 110 | + <artifactId>spring-cloud-function-context</artifactId> |
| 111 | + <groupId>org.springframework.cloud</groupId> | ||
| 112 | + </exclusion> | ||
| 113 | + <exclusion> | ||
| 114 | + <artifactId>spring-cloud-function-core</artifactId> | ||
| 115 | + <groupId>org.springframework.cloud</groupId> | ||
| 104 | </exclusion> | 116 | </exclusion> |
| 105 | </exclusions> | 117 | </exclusions> |
| 106 | </dependency> | 118 | </dependency> |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
| 1 | package cn.iocoder.yudao.framework.ai.config; | 1 | package cn.iocoder.yudao.framework.ai.config; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 4 | +import cn.hutool.extra.spring.SpringUtil; | ||
| 3 | import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; | 5 | import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; |
| 4 | import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl; | 6 | import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl; |
| 5 | import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; | 7 | import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; |
| 6 | -import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatOptions; | 8 | +import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel; |
| 9 | +import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel; | ||
| 7 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | 10 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; |
| 11 | +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel; | ||
| 8 | import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; | 12 | import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; |
| 9 | import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; | 13 | import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; |
| 10 | -import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatOptions; | ||
| 11 | -import com.alibaba.cloud.ai.tongyi.TongYiAutoConfiguration; | ||
| 12 | import lombok.extern.slf4j.Slf4j; | 14 | import lombok.extern.slf4j.Slf4j; |
| 15 | +import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientProperties; | ||
| 16 | +import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreProperties; | ||
| 17 | +import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties; | ||
| 18 | +import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; | ||
| 19 | +import org.springframework.ai.embedding.BatchingStrategy; | ||
| 20 | +import org.springframework.ai.embedding.TokenCountBatchingStrategy; | ||
| 21 | +import org.springframework.ai.model.tool.ToolCallingManager; | ||
| 22 | +import org.springframework.ai.openai.OpenAiChatModel; | ||
| 23 | +import org.springframework.ai.openai.OpenAiChatOptions; | ||
| 24 | +import org.springframework.ai.openai.api.OpenAiApi; | ||
| 13 | import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; | 25 | import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; |
| 14 | import org.springframework.ai.tokenizer.TokenCountEstimator; | 26 | import org.springframework.ai.tokenizer.TokenCountEstimator; |
| 15 | -import org.springframework.ai.transformer.splitter.TokenTextSplitter; | ||
| 16 | import org.springframework.boot.autoconfigure.AutoConfiguration; | 27 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 17 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | 28 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 18 | import org.springframework.boot.context.properties.EnableConfigurationProperties; | 29 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 19 | import org.springframework.context.annotation.Bean; | 30 | import org.springframework.context.annotation.Bean; |
| 20 | -import org.springframework.context.annotation.Import; | ||
| 21 | -import org.springframework.context.annotation.Lazy; | ||
| 22 | 31 | ||
| 23 | /** | 32 | /** |
| 24 | * 芋道 AI 自动配置 | 33 | * 芋道 AI 自动配置 |
| @@ -26,9 +35,12 @@ import org.springframework.context.annotation.Lazy; | @@ -26,9 +35,12 @@ import org.springframework.context.annotation.Lazy; | ||
| 26 | * @author fansili | 35 | * @author fansili |
| 27 | */ | 36 | */ |
| 28 | @AutoConfiguration | 37 | @AutoConfiguration |
| 29 | -@EnableConfigurationProperties(YudaoAiProperties.class) | 38 | +@EnableConfigurationProperties({ YudaoAiProperties.class, |
| 39 | + QdrantVectorStoreProperties.class, // 解析 Qdrant 配置 | ||
| 40 | + RedisVectorStoreProperties.class, // 解析 Redis 配置 | ||
| 41 | + MilvusVectorStoreProperties.class, MilvusServiceClientProperties.class // 解析 Milvus 配置 | ||
| 42 | +}) | ||
| 30 | @Slf4j | 43 | @Slf4j |
| 31 | -@Import(TongYiAutoConfiguration.class) | ||
| 32 | public class YudaoAiAutoConfiguration { | 44 | public class YudaoAiAutoConfiguration { |
| 33 | 45 | ||
| 34 | @Bean | 46 | @Bean |
| @@ -36,33 +48,148 @@ public class YudaoAiAutoConfiguration { | @@ -36,33 +48,148 @@ public class YudaoAiAutoConfiguration { | ||
| 36 | return new AiModelFactoryImpl(); | 48 | return new AiModelFactoryImpl(); |
| 37 | } | 49 | } |
| 38 | 50 | ||
| 39 | - | ||
| 40 | // ========== 各种 AI Client 创建 ========== | 51 | // ========== 各种 AI Client 创建 ========== |
| 41 | 52 | ||
| 42 | @Bean | 53 | @Bean |
| 43 | @ConditionalOnProperty(value = "yudao.ai.deepseek.enable", havingValue = "true") | 54 | @ConditionalOnProperty(value = "yudao.ai.deepseek.enable", havingValue = "true") |
| 44 | public DeepSeekChatModel deepSeekChatModel(YudaoAiProperties yudaoAiProperties) { | 55 | public DeepSeekChatModel deepSeekChatModel(YudaoAiProperties yudaoAiProperties) { |
| 45 | - YudaoAiProperties.DeepSeekProperties properties = yudaoAiProperties.getDeepSeek(); | ||
| 46 | - DeepSeekChatOptions options = DeepSeekChatOptions.builder() | ||
| 47 | - .model(properties.getModel()) | ||
| 48 | - .temperature(properties.getTemperature()) | ||
| 49 | - .maxTokens(properties.getMaxTokens()) | ||
| 50 | - .topP(properties.getTopP()) | 56 | + YudaoAiProperties.DeepSeekProperties properties = yudaoAiProperties.getDeepseek(); |
| 57 | + return buildDeepSeekChatModel(properties); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + public DeepSeekChatModel buildDeepSeekChatModel(YudaoAiProperties.DeepSeekProperties properties) { | ||
| 61 | + if (StrUtil.isEmpty(properties.getModel())) { | ||
| 62 | + properties.setModel(DeepSeekChatModel.MODEL_DEFAULT); | ||
| 63 | + } | ||
| 64 | + OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() | ||
| 65 | + .openAiApi(OpenAiApi.builder() | ||
| 66 | + .baseUrl(DeepSeekChatModel.BASE_URL) | ||
| 67 | + .apiKey(properties.getApiKey()) | ||
| 68 | + .build()) | ||
| 69 | + .defaultOptions(OpenAiChatOptions.builder() | ||
| 70 | + .model(properties.getModel()) | ||
| 71 | + .temperature(properties.getTemperature()) | ||
| 72 | + .maxTokens(properties.getMaxTokens()) | ||
| 73 | + .topP(properties.getTopP()) | ||
| 74 | + .build()) | ||
| 75 | + .toolCallingManager(getToolCallingManager()) | ||
| 76 | + .build(); | ||
| 77 | + return new DeepSeekChatModel(openAiChatModel); | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + @Bean | ||
| 81 | + @ConditionalOnProperty(value = "yudao.ai.doubao.enable", havingValue = "true") | ||
| 82 | + public DouBaoChatModel douBaoChatClient(YudaoAiProperties yudaoAiProperties) { | ||
| 83 | + YudaoAiProperties.DouBaoProperties properties = yudaoAiProperties.getDoubao(); | ||
| 84 | + return buildDouBaoChatClient(properties); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + public DouBaoChatModel buildDouBaoChatClient(YudaoAiProperties.DouBaoProperties properties) { | ||
| 88 | + if (StrUtil.isEmpty(properties.getModel())) { | ||
| 89 | + properties.setModel(DouBaoChatModel.MODEL_DEFAULT); | ||
| 90 | + } | ||
| 91 | + OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() | ||
| 92 | + .openAiApi(OpenAiApi.builder() | ||
| 93 | + .baseUrl(DouBaoChatModel.BASE_URL) | ||
| 94 | + .apiKey(properties.getApiKey()) | ||
| 95 | + .build()) | ||
| 96 | + .defaultOptions(OpenAiChatOptions.builder() | ||
| 97 | + .model(properties.getModel()) | ||
| 98 | + .temperature(properties.getTemperature()) | ||
| 99 | + .maxTokens(properties.getMaxTokens()) | ||
| 100 | + .topP(properties.getTopP()) | ||
| 101 | + .build()) | ||
| 102 | + .toolCallingManager(getToolCallingManager()) | ||
| 103 | + .build(); | ||
| 104 | + return new DouBaoChatModel(openAiChatModel); | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + @Bean | ||
| 108 | + @ConditionalOnProperty(value = "yudao.ai.siliconflow.enable", havingValue = "true") | ||
| 109 | + public SiliconFlowChatModel siliconFlowChatClient(YudaoAiProperties yudaoAiProperties) { | ||
| 110 | + YudaoAiProperties.SiliconFlowProperties properties = yudaoAiProperties.getSiliconflow(); | ||
| 111 | + return buildSiliconFlowChatClient(properties); | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + public SiliconFlowChatModel buildSiliconFlowChatClient(YudaoAiProperties.SiliconFlowProperties properties) { | ||
| 115 | + if (StrUtil.isEmpty(properties.getModel())) { | ||
| 116 | + properties.setModel(SiliconFlowChatModel.MODEL_DEFAULT); | ||
| 117 | + } | ||
| 118 | + OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() | ||
| 119 | + .openAiApi(OpenAiApi.builder() | ||
| 120 | + .baseUrl(SiliconFlowChatModel.BASE_URL) | ||
| 121 | + .apiKey(properties.getApiKey()) | ||
| 122 | + .build()) | ||
| 123 | + .defaultOptions(OpenAiChatOptions.builder() | ||
| 124 | + .model(properties.getModel()) | ||
| 125 | + .temperature(properties.getTemperature()) | ||
| 126 | + .maxTokens(properties.getMaxTokens()) | ||
| 127 | + .topP(properties.getTopP()) | ||
| 128 | + .build()) | ||
| 129 | + .toolCallingManager(getToolCallingManager()) | ||
| 51 | .build(); | 130 | .build(); |
| 52 | - return new DeepSeekChatModel(properties.getApiKey(), options); | 131 | + return new SiliconFlowChatModel(openAiChatModel); |
| 132 | + } | ||
| 133 | + | ||
| 134 | + @Bean | ||
| 135 | + @ConditionalOnProperty(value = "yudao.ai.hunyuan.enable", havingValue = "true") | ||
| 136 | + public HunYuanChatModel hunYuanChatClient(YudaoAiProperties yudaoAiProperties) { | ||
| 137 | + YudaoAiProperties.HunYuanProperties properties = yudaoAiProperties.getHunyuan(); | ||
| 138 | + return buildHunYuanChatClient(properties); | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + public HunYuanChatModel buildHunYuanChatClient(YudaoAiProperties.HunYuanProperties properties) { | ||
| 142 | + if (StrUtil.isEmpty(properties.getModel())) { | ||
| 143 | + properties.setModel(HunYuanChatModel.MODEL_DEFAULT); | ||
| 144 | + } | ||
| 145 | + // 特殊:由于混元大模型不提供 deepseek,而是通过知识引擎,所以需要区分下 URL | ||
| 146 | + if (StrUtil.isEmpty(properties.getBaseUrl())) { | ||
| 147 | + properties.setBaseUrl( | ||
| 148 | + StrUtil.startWithIgnoreCase(properties.getModel(), "deepseek") ? HunYuanChatModel.DEEP_SEEK_BASE_URL | ||
| 149 | + : HunYuanChatModel.BASE_URL); | ||
| 150 | + } | ||
| 151 | + // 创建 OpenAiChatModel、HunYuanChatModel 对象 | ||
| 152 | + OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() | ||
| 153 | + .openAiApi(OpenAiApi.builder() | ||
| 154 | + .baseUrl(properties.getBaseUrl()) | ||
| 155 | + .apiKey(properties.getApiKey()) | ||
| 156 | + .build()) | ||
| 157 | + .defaultOptions(OpenAiChatOptions.builder() | ||
| 158 | + .model(properties.getModel()) | ||
| 159 | + .temperature(properties.getTemperature()) | ||
| 160 | + .maxTokens(properties.getMaxTokens()) | ||
| 161 | + .topP(properties.getTopP()) | ||
| 162 | + .build()) | ||
| 163 | + .toolCallingManager(getToolCallingManager()) | ||
| 164 | + .build(); | ||
| 165 | + return new HunYuanChatModel(openAiChatModel); | ||
| 53 | } | 166 | } |
| 54 | 167 | ||
| 55 | @Bean | 168 | @Bean |
| 56 | @ConditionalOnProperty(value = "yudao.ai.xinghuo.enable", havingValue = "true") | 169 | @ConditionalOnProperty(value = "yudao.ai.xinghuo.enable", havingValue = "true") |
| 57 | public XingHuoChatModel xingHuoChatClient(YudaoAiProperties yudaoAiProperties) { | 170 | public XingHuoChatModel xingHuoChatClient(YudaoAiProperties yudaoAiProperties) { |
| 58 | YudaoAiProperties.XingHuoProperties properties = yudaoAiProperties.getXinghuo(); | 171 | YudaoAiProperties.XingHuoProperties properties = yudaoAiProperties.getXinghuo(); |
| 59 | - XingHuoChatOptions options = XingHuoChatOptions.builder() | ||
| 60 | - .model(properties.getModel()) | ||
| 61 | - .temperature(properties.getTemperature()) | ||
| 62 | - .maxTokens(properties.getMaxTokens()) | ||
| 63 | - .topK(properties.getTopK()) | 172 | + return buildXingHuoChatClient(properties); |
| 173 | + } | ||
| 174 | + | ||
| 175 | + public XingHuoChatModel buildXingHuoChatClient(YudaoAiProperties.XingHuoProperties properties) { | ||
| 176 | + if (StrUtil.isEmpty(properties.getModel())) { | ||
| 177 | + properties.setModel(XingHuoChatModel.MODEL_DEFAULT); | ||
| 178 | + } | ||
| 179 | + OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() | ||
| 180 | + .openAiApi(OpenAiApi.builder() | ||
| 181 | + .baseUrl(XingHuoChatModel.BASE_URL) | ||
| 182 | + .apiKey(properties.getAppKey() + ":" + properties.getSecretKey()) | ||
| 183 | + .build()) | ||
| 184 | + .defaultOptions(OpenAiChatOptions.builder() | ||
| 185 | + .model(properties.getModel()) | ||
| 186 | + .temperature(properties.getTemperature()) | ||
| 187 | + .maxTokens(properties.getMaxTokens()) | ||
| 188 | + .topP(properties.getTopP()) | ||
| 189 | + .build()) | ||
| 190 | + .toolCallingManager(getToolCallingManager()) | ||
| 64 | .build(); | 191 | .build(); |
| 65 | - return new XingHuoChatModel(properties.getAppKey(), properties.getSecretKey(), options); | 192 | + return new XingHuoChatModel(openAiChatModel); |
| 66 | } | 193 | } |
| 67 | 194 | ||
| 68 | @Bean | 195 | @Bean |
| @@ -78,44 +205,20 @@ public class YudaoAiAutoConfiguration { | @@ -78,44 +205,20 @@ public class YudaoAiAutoConfiguration { | ||
| 78 | return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl()); | 205 | return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl()); |
| 79 | } | 206 | } |
| 80 | 207 | ||
| 81 | - // ========== rag 相关 ========== | ||
| 82 | - // TODO @xin 免费版本 | ||
| 83 | -// @Bean | ||
| 84 | -// @Lazy // TODO 芋艿:临时注释,避免无法启动」 | ||
| 85 | -// public TransformersEmbeddingModel transformersEmbeddingClient() { | ||
| 86 | -// return new TransformersEmbeddingModel(MetadataMode.EMBED); | ||
| 87 | -// } | ||
| 88 | - | ||
| 89 | - /** | ||
| 90 | - * TODO @xin 默认版本先不弄,目前都先取对应的 EmbeddingModel | ||
| 91 | - */ | ||
| 92 | -// @Bean | ||
| 93 | -// @Lazy // TODO 芋艿:临时注释,避免无法启动 | ||
| 94 | -// public RedisVectorStore vectorStore(TransformersEmbeddingModel embeddingModel, RedisVectorStoreProperties properties, | ||
| 95 | -// RedisProperties redisProperties) { | ||
| 96 | -// var config = RedisVectorStore.RedisVectorStoreConfig.builder() | ||
| 97 | -// .withIndexName(properties.getIndex()) | ||
| 98 | -// .withPrefix(properties.getPrefix()) | ||
| 99 | -// .withMetadataFields(new RedisVectorStore.MetadataField("knowledgeId", Schema.FieldType.NUMERIC)) | ||
| 100 | -// .build(); | ||
| 101 | -// | ||
| 102 | -// RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel, | ||
| 103 | -// new JedisPooled(redisProperties.getHost(), redisProperties.getPort()), | ||
| 104 | -// properties.isInitializeSchema()); | ||
| 105 | -// redisVectorStore.afterPropertiesSet(); | ||
| 106 | -// return redisVectorStore; | ||
| 107 | -// } | ||
| 108 | - @Bean | ||
| 109 | - @Lazy // TODO 芋艿:临时注释,避免无法启动 | ||
| 110 | - public TokenTextSplitter tokenTextSplitter() { | ||
| 111 | - //TODO @xin 配置提取 | ||
| 112 | - return new TokenTextSplitter(500, 100, 5, 10000, true); | ||
| 113 | - } | 208 | + // ========== RAG 相关 ========== |
| 114 | 209 | ||
| 115 | @Bean | 210 | @Bean |
| 116 | - @Lazy // TODO 芋艿:临时注释,避免无法启动 | ||
| 117 | public TokenCountEstimator tokenCountEstimator() { | 211 | public TokenCountEstimator tokenCountEstimator() { |
| 118 | return new JTokkitTokenCountEstimator(); | 212 | return new JTokkitTokenCountEstimator(); |
| 119 | } | 213 | } |
| 120 | 214 | ||
| 215 | + @Bean | ||
| 216 | + public BatchingStrategy batchingStrategy() { | ||
| 217 | + return new TokenCountBatchingStrategy(); | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | + private static ToolCallingManager getToolCallingManager() { | ||
| 221 | + return SpringUtil.getBean(ToolCallingManager.class); | ||
| 222 | + } | ||
| 223 | + | ||
| 121 | } | 224 | } |
| 122 | \ No newline at end of file | 225 | \ No newline at end of file |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java
| @@ -16,11 +16,31 @@ public class YudaoAiProperties { | @@ -16,11 +16,31 @@ public class YudaoAiProperties { | ||
| 16 | /** | 16 | /** |
| 17 | * DeepSeek | 17 | * DeepSeek |
| 18 | */ | 18 | */ |
| 19 | - private DeepSeekProperties deepSeek; | 19 | + @SuppressWarnings("SpellCheckingInspection") |
| 20 | + private DeepSeekProperties deepseek; | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 字节豆包 | ||
| 24 | + */ | ||
| 25 | + @SuppressWarnings("SpellCheckingInspection") | ||
| 26 | + private DouBaoProperties doubao; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 腾讯混元 | ||
| 30 | + */ | ||
| 31 | + @SuppressWarnings("SpellCheckingInspection") | ||
| 32 | + private HunYuanProperties hunyuan; | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 硅基流动 | ||
| 36 | + */ | ||
| 37 | + @SuppressWarnings("SpellCheckingInspection") | ||
| 38 | + private SiliconFlowProperties siliconflow; | ||
| 20 | 39 | ||
| 21 | /** | 40 | /** |
| 22 | * 讯飞星火 | 41 | * 讯飞星火 |
| 23 | */ | 42 | */ |
| 43 | + @SuppressWarnings("SpellCheckingInspection") | ||
| 24 | private XingHuoProperties xinghuo; | 44 | private XingHuoProperties xinghuo; |
| 25 | 45 | ||
| 26 | /** | 46 | /** |
| @@ -31,33 +51,74 @@ public class YudaoAiProperties { | @@ -31,33 +51,74 @@ public class YudaoAiProperties { | ||
| 31 | /** | 51 | /** |
| 32 | * Suno 音乐 | 52 | * Suno 音乐 |
| 33 | */ | 53 | */ |
| 54 | + @SuppressWarnings("SpellCheckingInspection") | ||
| 34 | private SunoProperties suno; | 55 | private SunoProperties suno; |
| 35 | 56 | ||
| 36 | @Data | 57 | @Data |
| 37 | - public static class XingHuoProperties { | 58 | + public static class DeepSeekProperties { |
| 38 | 59 | ||
| 39 | private String enable; | 60 | private String enable; |
| 40 | - private String appId; | ||
| 41 | - private String appKey; | ||
| 42 | - private String secretKey; | 61 | + private String apiKey; |
| 43 | 62 | ||
| 44 | private String model; | 63 | private String model; |
| 45 | - private Float temperature; | 64 | + private Double temperature; |
| 46 | private Integer maxTokens; | 65 | private Integer maxTokens; |
| 47 | - private Integer topK; | 66 | + private Double topP; |
| 48 | 67 | ||
| 49 | } | 68 | } |
| 50 | 69 | ||
| 51 | @Data | 70 | @Data |
| 52 | - public static class DeepSeekProperties { | 71 | + public static class DouBaoProperties { |
| 72 | + | ||
| 73 | + private String enable; | ||
| 74 | + private String apiKey; | ||
| 75 | + | ||
| 76 | + private String model; | ||
| 77 | + private Double temperature; | ||
| 78 | + private Integer maxTokens; | ||
| 79 | + private Double topP; | ||
| 80 | + | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + @Data | ||
| 84 | + public static class HunYuanProperties { | ||
| 53 | 85 | ||
| 54 | private String enable; | 86 | private String enable; |
| 87 | + private String baseUrl; | ||
| 55 | private String apiKey; | 88 | private String apiKey; |
| 56 | 89 | ||
| 57 | private String model; | 90 | private String model; |
| 58 | - private Float temperature; | 91 | + private Double temperature; |
| 92 | + private Integer maxTokens; | ||
| 93 | + private Double topP; | ||
| 94 | + | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + @Data | ||
| 98 | + public static class SiliconFlowProperties { | ||
| 99 | + | ||
| 100 | + private String enable; | ||
| 101 | + private String apiKey; | ||
| 102 | + | ||
| 103 | + private String model; | ||
| 104 | + private Double temperature; | ||
| 105 | + private Integer maxTokens; | ||
| 106 | + private Double topP; | ||
| 107 | + | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + @Data | ||
| 111 | + public static class XingHuoProperties { | ||
| 112 | + | ||
| 113 | + private String enable; | ||
| 114 | + private String appId; | ||
| 115 | + private String appKey; | ||
| 116 | + private String secretKey; | ||
| 117 | + | ||
| 118 | + private String model; | ||
| 119 | + private Double temperature; | ||
| 59 | private Integer maxTokens; | 120 | private Integer maxTokens; |
| 60 | - private Float topP; | 121 | + private Double topP; |
| 61 | 122 | ||
| 62 | } | 123 | } |
| 63 | 124 |