mirror of
https://github.com/labring/FastGPT.git
synced 2026-05-05 01:02:59 +08:00
Fix secret (#6738)
* fix: nosql inject * fix: nosql * fix: ts * doc * fix: update feedbacksession
This commit is contained in:
@@ -142,7 +142,6 @@ FastGPT 项目特定的代码规范和约定:
|
|||||||
- **TypeScript 问题**: any 类型滥用、类型定义不完整、不安全断言
|
- **TypeScript 问题**: any 类型滥用、类型定义不完整、不安全断言
|
||||||
- **异步错误处理**: 未处理 Promise、错误信息丢失、静默失败
|
- **异步错误处理**: 未处理 Promise、错误信息丢失、静默失败
|
||||||
- **React 性能**: 不必要的重渲染、渲染中创建对象、缺少 memoization
|
- **React 性能**: 不必要的重渲染、渲染中创建对象、缺少 memoization
|
||||||
- **工作流节点**: isEntry 未重置、交互历史未清理、白名单遗漏
|
|
||||||
- **安全漏洞**: 注入攻击、XSS、文件上传漏洞
|
- **安全漏洞**: 注入攻击、XSS、文件上传漏洞
|
||||||
|
|
||||||
📖 **详细清单**: [common-issues-checklist.md](./common-issues-checklist.md)
|
📖 **详细清单**: [common-issues-checklist.md](./common-issues-checklist.md)
|
||||||
|
|||||||
@@ -7,10 +7,8 @@
|
|||||||
- [1. TypeScript 问题](#1-typescript-问题)
|
- [1. TypeScript 问题](#1-typescript-问题)
|
||||||
- [2. 异步错误处理问题](#2-异步错误处理问题)
|
- [2. 异步错误处理问题](#2-异步错误处理问题)
|
||||||
- [3. React 性能问题](#3-react-性能问题)
|
- [3. React 性能问题](#3-react-性能问题)
|
||||||
- [4. 工作流节点问题](#4-工作流节点问题)
|
- [4. 安全漏洞问题](#4-安全漏洞问题)
|
||||||
- [5. 安全漏洞问题](#5-安全漏洞问题)
|
- [5. 环境配置问题](#5-环境配置问题)
|
||||||
- [6. 代码重复问题](#6-代码重复问题)
|
|
||||||
- [7. 环境配置问题](#7-环境配置问题)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -377,169 +375,96 @@ const ExpensiveList = ({ items }: { items: Item[] }) => {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. 工作流节点问题
|
|
||||||
|
|
||||||
### 🔴 4.1 isEntry 标志未重置
|
## 4. 安全漏洞问题
|
||||||
|
|
||||||
**问题识别**:
|
### 🔴 4.1 NoSQL 注入 (接口入参风险)
|
||||||
- 交互节点执行逻辑中第二阶段没有设置 `node.isEntry = false`
|
|
||||||
- 节点可能重复执行
|
|
||||||
- 交互节点功能异常
|
|
||||||
|
|
||||||
**快速修复**:
|
**核心风险**: MongoDB 查询操作符 (`$gt`、`$where`、`$regex`、`$ne` 等) 可以通过 HTTP 请求体注入。当接口直接将入参透传到数据库查询时,攻击者可以构造恶意对象绕过权限校验或泄露数据。
|
||||||
```typescript
|
|
||||||
// ❌ 问题代码
|
|
||||||
export const dispatchInteractiveNode = async (props: Props) => {
|
|
||||||
const { isEntry } = props.node;
|
|
||||||
|
|
||||||
if (!isEntry) {
|
**典型攻击场景**:
|
||||||
return { interactive: { ... } };
|
```
|
||||||
}
|
// 攻击者发送的请求体
|
||||||
|
POST /api/login
|
||||||
// 处理用户输入
|
{ "username": { "$gt": "" }, "password": { "$gt": "" } }
|
||||||
return { data: { ... } };
|
// → MongoDB 查询变为 { username: { $gt: "" }, password: { $gt: "" } }
|
||||||
// 忘记重置 isEntry!
|
// → 匹配所有用户,绕过密码校验
|
||||||
};
|
|
||||||
|
|
||||||
// ✅ 修复方案
|
|
||||||
export const dispatchInteractiveNode = async (props: Props) => {
|
|
||||||
const { node, lastInteractive } = props;
|
|
||||||
const { isEntry } = node;
|
|
||||||
|
|
||||||
// 第一阶段: 返回交互请求
|
|
||||||
if (!isEntry || lastInteractive?.type !== 'interactiveType') {
|
|
||||||
return {
|
|
||||||
[DispatchNodeResponseKeyEnum.interactive]: {
|
|
||||||
type: 'interactiveType',
|
|
||||||
params: { /* ... */ }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第二阶段: 处理用户输入
|
|
||||||
node.isEntry = false; // 🔴 必须: 重置入口标志
|
|
||||||
|
|
||||||
return {
|
|
||||||
data: { /* ... */ },
|
|
||||||
[DispatchNodeResponseKeyEnum.rewriteHistories]: histories.slice(0, -2)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**审查建议**: 🔴 严重问题,必须修复
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔴 4.2 交互历史未清理
|
|
||||||
|
|
||||||
**问题识别**:
|
**问题识别**:
|
||||||
- 交互节点返回值中没有 `rewriteHistories`
|
- 接口入参未经 zod/类型校验直接传入查询条件
|
||||||
- 用户会看到交互过程中产生的临时消息
|
- 查询字段类型声明为 `any` 或 `object`
|
||||||
|
- 使用 `req.body.xxx` 直接拼入 `find()`、`findOne()`、`updateOne()` 等
|
||||||
|
- 动态构建查询对象时未限制字段类型为原始值
|
||||||
|
|
||||||
**快速修复**:
|
**高危模式**:
|
||||||
```typescript
|
```typescript
|
||||||
// ❌ 问题代码
|
// ❌ 危险: 入参直接作为查询字段值
|
||||||
export const dispatchInteractiveNode = async (props: Props) => {
|
const { username, password } = req.body;
|
||||||
// 处理用户输入后
|
await db.users.findOne({ username, password });
|
||||||
return {
|
|
||||||
data: { result: userInput }
|
|
||||||
// 忘记清理交互对话的历史记录
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// ✅ 修复方案
|
// ❌ 危险: 对象字段透传进查询
|
||||||
export const dispatchInteractiveNode = async (props: Props) => {
|
async function getUser({ filter }: { filter: object }) {
|
||||||
const { histories } = props;
|
return db.users.findOne(filter); // filter 可以是任意操作符
|
||||||
|
|
||||||
// 处理用户输入后
|
|
||||||
return {
|
|
||||||
data: { result: userInput },
|
|
||||||
// 移除交互对话的历史记录 (用户问题 + 系统响应 = 2条)
|
|
||||||
[DispatchNodeResponseKeyEnum.rewriteHistories]: histories.slice(0, -2)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
**审查建议**: 🔴 严重问题,必须修复
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔴 4.3 isEntry 白名单遗漏
|
|
||||||
|
|
||||||
**问题识别**:
|
|
||||||
- 新增交互节点但未更新 isEntry 白名单
|
|
||||||
- 节点在恢复时 isEntry 被重置,导致流程错误
|
|
||||||
|
|
||||||
**快速修复**:
|
|
||||||
```typescript
|
|
||||||
// ❌ 问题代码
|
|
||||||
// packages/service/core/workflow/dispatch/index.ts
|
|
||||||
|
|
||||||
runtimeNodes.forEach((item) => {
|
|
||||||
if (
|
|
||||||
item.flowNodeType !== FlowNodeTypeEnum.userSelect &&
|
|
||||||
item.flowNodeType !== FlowNodeTypeEnum.formInput
|
|
||||||
// 新的交互节点类型未添加到白名单
|
|
||||||
) {
|
|
||||||
item.isEntry = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ✅ 修复方案
|
|
||||||
runtimeNodes.forEach((item) => {
|
|
||||||
if (
|
|
||||||
item.flowNodeType !== FlowNodeTypeEnum.userSelect &&
|
|
||||||
item.flowNodeType !== FlowNodeTypeEnum.formInput &&
|
|
||||||
item.flowNodeType !== FlowNodeTypeEnum.yourNodeType // 新增
|
|
||||||
) {
|
|
||||||
item.isEntry = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
**审查建议**: 🔴 严重问题,必须修复
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. 安全漏洞问题
|
|
||||||
|
|
||||||
### 🔴 5.1 SQL/NoSQL 注入
|
|
||||||
|
|
||||||
**问题识别**:
|
|
||||||
- 用户输入直接用于数据库查询
|
|
||||||
- 没有输入验证和清理
|
|
||||||
- 使用字符串拼接构建查询
|
|
||||||
|
|
||||||
**快速修复**:
|
|
||||||
```typescript
|
|
||||||
// ❌ 问题代码
|
|
||||||
async function searchUsers(query: string) {
|
|
||||||
return await db.users.find({ name: query });
|
|
||||||
// 如果 query = { "$gt": "" },会返回所有用户
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ 修复方案
|
// ❌ 危险: updateOne 条件字段未校验
|
||||||
async function searchUsers(query: string): Promise<User[]> {
|
await db.collection.updateOne(
|
||||||
if (!query || query.length > 100) {
|
{ _id: req.body.id }, // id 可能是 { $gt: "" }
|
||||||
|
{ $set: req.body.update } // update 可能注入 $where 等
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**快速修复**:
|
||||||
|
```typescript
|
||||||
|
// ✅ 方案 1: zod schema 严格约束入参类型(推荐)
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const LoginSchema = z.object({
|
||||||
|
username: z.string().min(1).max(50),
|
||||||
|
password: z.string().min(1).max(100)
|
||||||
|
});
|
||||||
|
|
||||||
|
async function login(req: Request) {
|
||||||
|
// parse 失败直接抛出,不会进入查询逻辑
|
||||||
|
const { username, password } = LoginSchema.parse(req.body);
|
||||||
|
return db.users.findOne({ username, password });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 方案 2: 显式提取原始值,拒绝对象类型
|
||||||
|
async function searchUsers(query: unknown): Promise<User[]> {
|
||||||
|
if (typeof query !== 'string' || query.length > 100) {
|
||||||
throw new Error('Invalid query');
|
throw new Error('Invalid query');
|
||||||
}
|
}
|
||||||
|
return db.users.find({
|
||||||
const sanitizedQuery = query.replace(/[^\w\s]/g, '');
|
name: { $regex: query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), $options: 'i' }
|
||||||
|
|
||||||
return await db.users.find({
|
|
||||||
name: {
|
|
||||||
$regex: sanitizedQuery,
|
|
||||||
$options: 'i'
|
|
||||||
}
|
|
||||||
}).limit(10).toArray();
|
}).limit(10).toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ 方案 3: 动态查询条件使用白名单字段
|
||||||
|
const ALLOWED_FILTER_FIELDS = ['status', 'type', 'teamId'] as const;
|
||||||
|
|
||||||
|
function buildSafeFilter(raw: Record<string, unknown>) {
|
||||||
|
return ALLOWED_FILTER_FIELDS.reduce((acc, key) => {
|
||||||
|
if (raw[key] !== undefined && typeof raw[key] === 'string') {
|
||||||
|
acc[key] = raw[key];
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<string, string>);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**审查重点**:
|
||||||
|
- [ ] 所有接口入参是否经过 zod schema 或等效校验
|
||||||
|
- [ ] 查询条件字段是否均为原始类型 (`string`、`number`、`boolean`)
|
||||||
|
- [ ] 是否存在将 `req.body` 的对象字段直接传入 MongoDB 操作符位置的情况
|
||||||
|
- [ ] `_id` 字段是否使用 `new Types.ObjectId(id)` 强制转换
|
||||||
|
|
||||||
**审查建议**: 🔴 严重问题,必须修复
|
**审查建议**: 🔴 严重问题,必须修复
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 🔴 5.2 XSS 攻击
|
### 🔴 4.2 XSS 攻击
|
||||||
|
|
||||||
**问题识别**:
|
**问题识别**:
|
||||||
- 使用 `dangerouslySetInnerHTML`
|
- 使用 `dangerouslySetInnerHTML`
|
||||||
@@ -587,7 +512,7 @@ const UserProfile = ({ user }: { user: User }) => {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 🔴 5.3 文件上传漏洞
|
### 🔴 4.3 文件上传漏洞
|
||||||
|
|
||||||
**问题识别**:
|
**问题识别**:
|
||||||
- 没有文件类型验证
|
- 没有文件类型验证
|
||||||
@@ -640,99 +565,10 @@ app.post('/upload', async (req, res) => {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. 代码重复问题
|
|
||||||
|
|
||||||
### 🟡 6.1 重复的逻辑
|
## 5. 环境配置问题
|
||||||
|
|
||||||
**问题识别**:
|
### 🔴 5.1 硬编码配置
|
||||||
- 相同或相似的代码出现在多处
|
|
||||||
- 复制粘贴的代码
|
|
||||||
- 修改 bug 时需要改多处
|
|
||||||
|
|
||||||
**快速修复**:
|
|
||||||
```typescript
|
|
||||||
// ❌ 问题代码
|
|
||||||
function validateEmail1(email: string): boolean {
|
|
||||||
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateEmail2(email: string): boolean {
|
|
||||||
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ 修复方案
|
|
||||||
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
||||||
|
|
||||||
function validateEmail(email: string): boolean {
|
|
||||||
return EMAIL_REGEX.test(email);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**审查建议**: 🟡 建议改进
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🟡 6.2 重复的组件结构
|
|
||||||
|
|
||||||
**问题识别**:
|
|
||||||
- 多个组件有相似的结构和布局
|
|
||||||
- 只有细微差别
|
|
||||||
- 可以抽取共享逻辑或样式
|
|
||||||
|
|
||||||
**快速修复**:
|
|
||||||
```typescript
|
|
||||||
// ❌ 问题代码
|
|
||||||
const UserList1 = ({ users }: { users: User[] }) => {
|
|
||||||
return (
|
|
||||||
<Box p={4} borderWidth="1px" borderRadius="md">
|
|
||||||
<VStack spacing={3}>
|
|
||||||
{users.map(user => (
|
|
||||||
<Box key={user.id} p={3} bg="gray.100">
|
|
||||||
<Text>{user.name}</Text>
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</VStack>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ✅ 修复方案
|
|
||||||
interface ListProps<T> {
|
|
||||||
items: T[];
|
|
||||||
renderItem: (item: T) => React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
const GenericList = <T,>({ items, renderItem }: ListProps<T>) => {
|
|
||||||
return (
|
|
||||||
<Box p={4} borderWidth="1px" borderRadius="md">
|
|
||||||
<VStack spacing={3}>
|
|
||||||
{items.map((item, index) => (
|
|
||||||
<Box key={index} p={3} bg="gray.100">
|
|
||||||
{renderItem(item)}
|
|
||||||
</Box>
|
|
||||||
))}
|
|
||||||
</VStack>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const UserList = ({ users }: { users: User[] }) => {
|
|
||||||
return (
|
|
||||||
<GenericList
|
|
||||||
items={users}
|
|
||||||
renderItem={(user) => <Text>{user.name}</Text>}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
**审查建议**: 🟡 建议改进
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. 环境配置问题
|
|
||||||
|
|
||||||
### 🔴 7.1 硬编码配置
|
|
||||||
|
|
||||||
**问题识别**:
|
**问题识别**:
|
||||||
- 配置值直接写在代码中
|
- 配置值直接写在代码中
|
||||||
@@ -758,7 +594,7 @@ if (!API_KEY) {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 🟡 7.2 环境变量未验证
|
### 🟡 5.2 环境变量未验证
|
||||||
|
|
||||||
**问题识别**:
|
**问题识别**:
|
||||||
- 直接使用环境变量而不验证
|
- 直接使用环境变量而不验证
|
||||||
@@ -806,7 +642,6 @@ const config = getConfig();
|
|||||||
|
|
||||||
- [ ] 滥用 `any` 类型
|
- [ ] 滥用 `any` 类型
|
||||||
- [ ] 未处理的 Promise rejection
|
- [ ] 未处理的 Promise rejection
|
||||||
- [ ] 工作流节点 `isEntry` 未重置
|
|
||||||
- [ ] 硬编码敏感信息
|
- [ ] 硬编码敏感信息
|
||||||
- [ ] SQL/NoSQL 注入漏洞
|
- [ ] SQL/NoSQL 注入漏洞
|
||||||
- [ ] XSS 攻击漏洞
|
- [ ] XSS 攻击漏洞
|
||||||
@@ -818,7 +653,6 @@ const config = getConfig();
|
|||||||
- [ ] 错误信息丢失
|
- [ ] 错误信息丢失
|
||||||
- [ ] React 不必要的重渲染
|
- [ ] React 不必要的重渲染
|
||||||
- [ ] 环境变量未验证
|
- [ ] 环境变量未验证
|
||||||
- [ ] 代码重复
|
|
||||||
|
|
||||||
### 🟢 可选优化 (锦上添花)
|
### 🟢 可选优化 (锦上添花)
|
||||||
|
|
||||||
|
|||||||
@@ -13,4 +13,5 @@ description: 'FastGPT V4.14.11 更新说明'
|
|||||||
## 🐛 修复
|
## 🐛 修复
|
||||||
|
|
||||||
1. 对话 Agent 模式,模型存在刷新后被重置问题。
|
1. 对话 Agent 模式,模型存在刷新后被重置问题。
|
||||||
2. 部分接口未正确进行权限校验。
|
2. 部分接口未正确进行权限校验。
|
||||||
|
3. 修复部分接口 nosql 注入分析。
|
||||||
@@ -222,7 +222,7 @@
|
|||||||
"document/content/docs/self-host/upgrading/4-14/4141.mdx": "2026-03-03T17:39:47+08:00",
|
"document/content/docs/self-host/upgrading/4-14/4141.mdx": "2026-03-03T17:39:47+08:00",
|
||||||
"document/content/docs/self-host/upgrading/4-14/41410.en.mdx": "2026-03-31T23:15:29+08:00",
|
"document/content/docs/self-host/upgrading/4-14/41410.en.mdx": "2026-03-31T23:15:29+08:00",
|
||||||
"document/content/docs/self-host/upgrading/4-14/41410.mdx": "2026-04-08T16:15:25+08:00",
|
"document/content/docs/self-host/upgrading/4-14/41410.mdx": "2026-04-08T16:15:25+08:00",
|
||||||
"document/content/docs/self-host/upgrading/4-14/41411.mdx": "2026-04-07T21:48:43+08:00",
|
"document/content/docs/self-host/upgrading/4-14/41411.mdx": "2026-04-09T15:12:39+08:00",
|
||||||
"document/content/docs/self-host/upgrading/4-14/4142.en.mdx": "2026-03-03T17:39:47+08:00",
|
"document/content/docs/self-host/upgrading/4-14/4142.en.mdx": "2026-03-03T17:39:47+08:00",
|
||||||
"document/content/docs/self-host/upgrading/4-14/4142.mdx": "2026-03-03T17:39:47+08:00",
|
"document/content/docs/self-host/upgrading/4-14/4142.mdx": "2026-03-03T17:39:47+08:00",
|
||||||
"document/content/docs/self-host/upgrading/4-14/4143.en.mdx": "2026-03-03T17:39:47+08:00",
|
"document/content/docs/self-host/upgrading/4-14/4143.en.mdx": "2026-03-03T17:39:47+08:00",
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ import type {
|
|||||||
YuqueServer
|
YuqueServer
|
||||||
} from './apiDataset/type';
|
} from './apiDataset/type';
|
||||||
import type { SourceMemberType } from '../../support/user/type';
|
import type { SourceMemberType } from '../../support/user/type';
|
||||||
import type { DatasetDataIndexTypeEnum } from './data/constants';
|
import { DatasetDataIndexTypeEnum } from './data/constants';
|
||||||
import type { ParentIdType } from '../../common/parentFolder/type';
|
import type { ParentIdType } from '../../common/parentFolder/type';
|
||||||
|
import z from 'zod';
|
||||||
|
import { ObjectIdSchema } from '../../common/type/mongo';
|
||||||
|
|
||||||
export type ChunkSettingsType = {
|
export type ChunkSettingsType = {
|
||||||
trainingType?: DatasetCollectionDataProcessModeEnum;
|
trainingType?: DatasetCollectionDataProcessModeEnum;
|
||||||
@@ -146,35 +148,42 @@ export type DatasetCollectionTagsSchemaType = {
|
|||||||
tag: string;
|
tag: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DatasetDataIndexItemType = {
|
export const DatasetDataIndexItemSchema = z.object({
|
||||||
type: `${DatasetDataIndexTypeEnum}`;
|
type: z.enum(DatasetDataIndexTypeEnum).meta({ description: '索引类型' }),
|
||||||
dataId: string; // pg data id
|
dataId: z.string().meta({ description: 'vectorDB ID' }),
|
||||||
text: string;
|
text: z.string().meta({ description: '索引文本' })
|
||||||
};
|
});
|
||||||
|
export type DatasetDataIndexItemType = z.infer<typeof DatasetDataIndexItemSchema>;
|
||||||
|
|
||||||
export type DatasetDataFieldType = {
|
export const DatasetDataFieldSchema = z.object({
|
||||||
q: string; // large chunks or question
|
q: z.string().meta({ description: '问题/主文本' }),
|
||||||
a?: string; // answer or custom content
|
a: z.string().optional().meta({ description: '回答/补充文本' }),
|
||||||
imageId?: string;
|
imageId: z.string().optional().meta({ description: '图片 ID' })
|
||||||
};
|
});
|
||||||
export type DatasetDataSchemaType = DatasetDataFieldType & {
|
export type DatasetDataFieldType = z.infer<typeof DatasetDataFieldSchema>;
|
||||||
_id: string;
|
|
||||||
userId: string;
|
export const DatasetDataHistorySchema = DatasetDataFieldSchema.extend({
|
||||||
teamId: string;
|
updateTime: z.date().meta({ description: '更新时间' })
|
||||||
tmbId: string;
|
});
|
||||||
datasetId: string;
|
export type DatasetDataHistoryType = z.infer<typeof DatasetDataHistorySchema>;
|
||||||
collectionId: string;
|
|
||||||
chunkIndex: number;
|
export const DatasetDataSchema = DatasetDataFieldSchema.extend({
|
||||||
updateTime: Date;
|
_id: ObjectIdSchema.meta({ description: '数据 ID' }),
|
||||||
history?: (DatasetDataFieldType & {
|
userId: ObjectIdSchema.meta({ description: '用户 ID' }),
|
||||||
updateTime: Date;
|
teamId: ObjectIdSchema.meta({ description: '团队 ID' }),
|
||||||
})[];
|
tmbId: ObjectIdSchema.meta({ description: '团队成员 ID' }),
|
||||||
forbid?: boolean;
|
datasetId: ObjectIdSchema.meta({ description: '数据集 ID' }),
|
||||||
fullTextToken: string;
|
collectionId: ObjectIdSchema.meta({ description: '集合 ID' }),
|
||||||
indexes: DatasetDataIndexItemType[];
|
chunkIndex: z.int().min(0).meta({ description: '块索引' }),
|
||||||
rebuilding?: boolean;
|
updateTime: z.date().meta({ description: '更新时间' }),
|
||||||
imageDescMap?: Record<string, string>;
|
history: z.array(DatasetDataHistorySchema).optional().meta({ description: '历史版本' }),
|
||||||
};
|
forbid: z.boolean().optional().meta({ description: '是否禁用' }),
|
||||||
|
fullTextToken: z.string().meta({ description: '全文 token' }),
|
||||||
|
indexes: z.array(DatasetDataIndexItemSchema).meta({ description: '向量索引' }),
|
||||||
|
rebuilding: z.boolean().optional().meta({ description: '重建中' }),
|
||||||
|
imageDescMap: z.record(z.string(), z.string()).optional().meta({ description: '图片描述映射' })
|
||||||
|
});
|
||||||
|
export type DatasetDataSchemaType = z.infer<typeof DatasetDataSchema>;
|
||||||
|
|
||||||
export type DatasetDataTextSchemaType = {
|
export type DatasetDataTextSchemaType = {
|
||||||
_id: string;
|
_id: string;
|
||||||
@@ -308,13 +317,18 @@ export type SearchDataResponseItemType = Omit<
|
|||||||
// score: number;
|
// score: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DatasetCiteItemType = {
|
export const DatasetCiteItemSchema = z
|
||||||
_id: string;
|
.object({
|
||||||
q: string;
|
_id: ObjectIdSchema.meta({ description: '数据 ID' }),
|
||||||
a?: string;
|
q: z.string().meta({ description: '问题/主文本' }),
|
||||||
imagePreivewUrl?: string;
|
a: z.string().optional().meta({ description: '回答/补充文本' }),
|
||||||
history?: DatasetDataSchemaType['history'];
|
imagePreivewUrl: z.string().optional().meta({ description: '图片预览 URL' }),
|
||||||
updateTime: DatasetDataSchemaType['updateTime'];
|
history: DatasetDataSchema.shape.history.optional(),
|
||||||
index: DatasetDataSchemaType['chunkIndex'];
|
updateTime: DatasetDataSchema.shape.updateTime,
|
||||||
updated?: boolean;
|
index: DatasetDataSchema.shape.chunkIndex,
|
||||||
};
|
updated: z.boolean().optional().meta({ description: '是否已更新' })
|
||||||
|
})
|
||||||
|
.meta({
|
||||||
|
description: '知识库引用数据列表'
|
||||||
|
});
|
||||||
|
export type DatasetCiteItemType = z.infer<typeof DatasetCiteItemSchema>;
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
import { OutLinkChatAuthSchema } from '../../../../support/permission/chat';
|
||||||
|
import { ChatMessageSchema } from '../api';
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* API: 创建问题引导
|
||||||
|
* Route: POST /api/core/ai/agent/createQuestionGuide
|
||||||
|
* Method: POST
|
||||||
|
* Description: 根据对话历史生成推荐的引导问题列表
|
||||||
|
* Tags: ['AI', 'Agent', 'Read']
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
export const CreateQuestionGuideBodySchema = OutLinkChatAuthSchema.extend({
|
||||||
|
messages: z.array(ChatMessageSchema).meta({
|
||||||
|
description: '对话历史消息列表'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export type CreateQuestionGuideBodyType = z.infer<typeof CreateQuestionGuideBodySchema>;
|
||||||
|
|
||||||
|
export const CreateQuestionGuideResponseSchema = z.array(z.string()).meta({
|
||||||
|
example: ['你能帮我做什么?', '如何使用这个功能?'],
|
||||||
|
description: '推荐的引导问题列表'
|
||||||
|
});
|
||||||
|
|
||||||
|
export type CreateQuestionGuideResponseType = z.infer<typeof CreateQuestionGuideResponseSchema>;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import type { OpenAPIPath } from '../../../type';
|
||||||
|
import { TagsMap } from '../../../tag';
|
||||||
|
import { CreateQuestionGuideBodySchema, CreateQuestionGuideResponseSchema } from './api';
|
||||||
|
|
||||||
|
export const AgentPath: OpenAPIPath = {
|
||||||
|
'/core/ai/agent/createQuestionGuide': {
|
||||||
|
post: {
|
||||||
|
summary: '创建问题引导',
|
||||||
|
description: '根据对话历史生成推荐的引导问题列表',
|
||||||
|
tags: [TagsMap.aiCommon],
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: CreateQuestionGuideBodySchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: '成功返回推荐的引导问题列表',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: CreateQuestionGuideResponseSchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -31,3 +31,24 @@ export const LLMRequestRecordSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export type LLMRequestRecordSchemaType = z.infer<typeof LLMRequestRecordSchema>;
|
export type LLMRequestRecordSchemaType = z.infer<typeof LLMRequestRecordSchema>;
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* 共享 Schema
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
export const ChatMessageSchema = z.object({
|
||||||
|
role: z.enum(['user', 'assistant', 'system', 'tool', 'function']).meta({
|
||||||
|
example: 'user',
|
||||||
|
description: '消息角色'
|
||||||
|
}),
|
||||||
|
content: z
|
||||||
|
.union([z.string(), z.array(z.object())])
|
||||||
|
.optional()
|
||||||
|
.meta({
|
||||||
|
example: '你好',
|
||||||
|
description: '消息内容'
|
||||||
|
}),
|
||||||
|
name: z.string().optional().meta({ description: '发送者名称' }),
|
||||||
|
tool_calls: z.array(z.object()).optional().meta({ description: '工具调用' }),
|
||||||
|
tool_call_id: z.string().optional().meta({ description: '工具调用 ID' })
|
||||||
|
});
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ import type { OpenAPIPath } from '../../type';
|
|||||||
import { TagsMap } from '../../tag';
|
import { TagsMap } from '../../tag';
|
||||||
import { GetLLMRequestRecordParamsSchema, LLMRequestRecordSchema } from './api';
|
import { GetLLMRequestRecordParamsSchema, LLMRequestRecordSchema } from './api';
|
||||||
import { SandboxPath } from './sandbox';
|
import { SandboxPath } from './sandbox';
|
||||||
|
import { AgentPath } from './agent';
|
||||||
|
|
||||||
export const AIPath: OpenAPIPath = {
|
export const AIPath: OpenAPIPath = {
|
||||||
...SandboxPath,
|
...SandboxPath,
|
||||||
|
...AgentPath,
|
||||||
|
|
||||||
'/core/ai/record/getRecord': {
|
'/core/ai/record/getRecord': {
|
||||||
get: {
|
get: {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { GetRecentlyUsedAppsResponseSchema } from './api';
|
|||||||
import { TagsMap } from '../../tag';
|
import { TagsMap } from '../../tag';
|
||||||
import { ChatControllerPath } from './controler';
|
import { ChatControllerPath } from './controler';
|
||||||
import { HelperBotPath } from './helperBot';
|
import { HelperBotPath } from './helperBot';
|
||||||
|
import { ChatQuotePath } from './quote/index';
|
||||||
|
import { ChatInputGuidePath } from './inputGuide/index';
|
||||||
|
|
||||||
export const ChatPath: OpenAPIPath = {
|
export const ChatPath: OpenAPIPath = {
|
||||||
...ChatSettingPath,
|
...ChatSettingPath,
|
||||||
@@ -15,6 +17,8 @@ export const ChatPath: OpenAPIPath = {
|
|||||||
...ChatHistoryPath,
|
...ChatHistoryPath,
|
||||||
...ChatControllerPath,
|
...ChatControllerPath,
|
||||||
...HelperBotPath,
|
...HelperBotPath,
|
||||||
|
...ChatQuotePath,
|
||||||
|
...ChatInputGuidePath,
|
||||||
|
|
||||||
'/core/chat/recentlyUsed': {
|
'/core/chat/recentlyUsed': {
|
||||||
get: {
|
get: {
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
import { PaginationSchema } from '../../../api';
|
||||||
|
import { ObjectIdSchema } from '../../../../common/type/mongo';
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* API: 获取对话输入引导列表
|
||||||
|
* Route: POST /api/core/chat/inputGuide/list
|
||||||
|
* Method: POST
|
||||||
|
* Description: 获取指定应用的对话输入引导列表,支持关键词搜索
|
||||||
|
* Tags: ['Chat', 'InputGuide', 'Read']
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
export const ChatInputGuideListBodySchema = PaginationSchema.extend({
|
||||||
|
appId: ObjectIdSchema.describe('应用 ID'),
|
||||||
|
searchKey: z.string().max(200).optional().default('').meta({
|
||||||
|
example: '如何使用',
|
||||||
|
description: '搜索关键词,用于模糊匹配引导文本'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
export type ChatInputGuideListBodyType = z.infer<typeof ChatInputGuideListBodySchema>;
|
||||||
|
|
||||||
|
export const ChatInputGuideItemSchema = z.object({
|
||||||
|
_id: z.coerce.string().meta({ example: '68ad85a7463006c963799a05', description: '引导 ID' }),
|
||||||
|
appId: z.coerce.string().meta({ example: '68ad85a7463006c963799a06', description: '应用 ID' }),
|
||||||
|
text: z.string().meta({ example: '如何开始使用?', description: '引导文本' })
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ChatInputGuideListResponseSchema = z.object({
|
||||||
|
list: z.array(ChatInputGuideItemSchema).meta({ description: '引导列表' }),
|
||||||
|
total: z.number().meta({ example: 10, description: '总数' })
|
||||||
|
});
|
||||||
|
export type ChatInputGuideListResponseType = z.infer<typeof ChatInputGuideListResponseSchema>;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import type { OpenAPIPath } from '../../../type';
|
||||||
|
import { TagsMap } from '../../../tag';
|
||||||
|
import { ChatInputGuideListBodySchema, ChatInputGuideListResponseSchema } from './api';
|
||||||
|
|
||||||
|
export const ChatInputGuidePath: OpenAPIPath = {
|
||||||
|
'/core/chat/inputGuide/list': {
|
||||||
|
post: {
|
||||||
|
summary: '获取对话输入引导列表',
|
||||||
|
description: '获取指定应用的对话输入引导列表,支持关键词模糊搜索和分页',
|
||||||
|
tags: [TagsMap.chatInputGuide],
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: ChatInputGuideListBodySchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: '成功返回输入引导列表',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: ChatInputGuideListResponseSchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
import { ObjectIdSchema } from '../../../../common/type/mongo';
|
||||||
|
import { OutLinkChatAuthSchema } from '../../../../support/permission/chat';
|
||||||
|
import { DatasetCiteItemSchema } from '../../../../core/dataset/type';
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* API: 获取对话引用数据
|
||||||
|
* Route: POST /api/core/chat/quote/getQuote
|
||||||
|
* Method: POST
|
||||||
|
* Description: 获取指定对话消息的数据集引用列表
|
||||||
|
* Tags: ['Chat', 'Quote', 'Read']
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
export const GetQuoteBodySchema = OutLinkChatAuthSchema.extend({
|
||||||
|
appId: ObjectIdSchema.describe('应用 ID'),
|
||||||
|
chatId: z.string().min(1).max(256).meta({
|
||||||
|
example: 'chat_abc123',
|
||||||
|
description: '对话 ID'
|
||||||
|
}),
|
||||||
|
chatItemDataId: z.string().min(1).max(256).meta({
|
||||||
|
example: 'item_abc123',
|
||||||
|
description: '对话消息 dataId'
|
||||||
|
}),
|
||||||
|
datasetDataIdList: z
|
||||||
|
.array(z.string().min(1).max(256))
|
||||||
|
.max(200)
|
||||||
|
.meta({
|
||||||
|
example: ['68ad85a7463006c963799a05'],
|
||||||
|
description: '数据集数据 ID 列表'
|
||||||
|
}),
|
||||||
|
collectionIdList: z
|
||||||
|
.array(z.string().min(1).max(256))
|
||||||
|
.max(200)
|
||||||
|
.meta({
|
||||||
|
example: ['68ad85a7463006c963799a06'],
|
||||||
|
description: '集合 ID 列表'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
export type GetQuoteBodyType = z.infer<typeof GetQuoteBodySchema>;
|
||||||
|
|
||||||
|
export const GetQuoteResponseSchema = z.array(DatasetCiteItemSchema);
|
||||||
|
export type GetQuoteResponseType = z.infer<typeof GetQuoteResponseSchema>;
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* API: 获取集合分页引用数据
|
||||||
|
* Route: POST /api/core/chat/quote/getCollectionQuote
|
||||||
|
* Method: POST
|
||||||
|
* Description: 以链式分页方式获取指定集合的引用数据,支持前后翻页
|
||||||
|
* Tags: ['Chat', 'Quote', 'Read']
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
export const GetCollectionQuoteBodySchema = OutLinkChatAuthSchema.extend({
|
||||||
|
appId: ObjectIdSchema.describe('应用 ID'),
|
||||||
|
chatId: z.string().min(1).max(256).describe('对话 ID'),
|
||||||
|
chatItemDataId: z.string().min(1).max(256).describe('对话消息 dataId'),
|
||||||
|
collectionId: ObjectIdSchema.describe('集合 ID'),
|
||||||
|
pageSize: z.number().int().min(1).max(30).default(15).describe('每页条数,范围 [1, 30]'),
|
||||||
|
anchor: z.number().optional().describe('当前锚点 chunkIndex'),
|
||||||
|
initialId: z.string().optional().describe('初始定位数据 ID'),
|
||||||
|
nextId: z.string().optional().describe('向后翻页的游标 ID'),
|
||||||
|
prevId: z.string().optional().describe('向前翻页的游标 ID')
|
||||||
|
});
|
||||||
|
export type GetCollectionQuoteBodyType = z.infer<typeof GetCollectionQuoteBodySchema>;
|
||||||
|
|
||||||
|
export const GetCollectionQuoteResSchema = z.object({
|
||||||
|
list: z.array(
|
||||||
|
DatasetCiteItemSchema.extend({
|
||||||
|
id: z.string().describe('数据 ID(alias _id)'),
|
||||||
|
anchor: z.number().optional().describe('chunk 序号(alias index)')
|
||||||
|
})
|
||||||
|
),
|
||||||
|
hasMorePrev: z.boolean().describe('是否还有更多前置数据'),
|
||||||
|
hasMoreNext: z.boolean().describe('是否还有更多后置数据')
|
||||||
|
});
|
||||||
|
export type GetCollectionQuoteResType = z.infer<typeof GetCollectionQuoteResSchema>;
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import type { OpenAPIPath } from '../../../type';
|
||||||
|
import { TagsMap } from '../../../tag';
|
||||||
|
import {
|
||||||
|
GetQuoteBodySchema,
|
||||||
|
GetQuoteResponseSchema,
|
||||||
|
GetCollectionQuoteBodySchema,
|
||||||
|
GetCollectionQuoteResSchema
|
||||||
|
} from './api';
|
||||||
|
|
||||||
|
export const ChatQuotePath: OpenAPIPath = {
|
||||||
|
'/core/chat/quote/getQuote': {
|
||||||
|
post: {
|
||||||
|
summary: '获取对话引用数据',
|
||||||
|
description: '获取指定对话消息的数据集引用列表,需要对话访问权限',
|
||||||
|
tags: [TagsMap.chatPage],
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: GetQuoteBodySchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: '成功返回引用数据列表',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: GetQuoteResponseSchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'/core/chat/quote/getCollectionQuote': {
|
||||||
|
post: {
|
||||||
|
summary: '获取集合分页引用数据',
|
||||||
|
description: '以链式分页方式获取指定集合的引用数据,支持前后翻页,需要对话访问权限',
|
||||||
|
tags: [TagsMap.chatPage],
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: GetCollectionQuoteBodySchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: '成功返回分页引用数据',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: GetCollectionQuoteResSchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -23,6 +23,7 @@ export const TagsMap = {
|
|||||||
chatController: '对话操作',
|
chatController: '对话操作',
|
||||||
chatFeedback: '对话反馈',
|
chatFeedback: '对话反馈',
|
||||||
chatSetting: '门户页配置',
|
chatSetting: '门户页配置',
|
||||||
|
chatInputGuide: '对话输入引导',
|
||||||
|
|
||||||
// Dataset
|
// Dataset
|
||||||
datasetCollection: '集合',
|
datasetCollection: '集合',
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import { Box, Flex, HStack } from '@chakra-ui/react';
|
import { Box, Flex, HStack } from '@chakra-ui/react';
|
||||||
import {
|
import { type SearchDataResponseItemType } from '@fastgpt/global/core/dataset/type';
|
||||||
type DatasetCiteItemType,
|
|
||||||
type SearchDataResponseItemType
|
|
||||||
} from '@fastgpt/global/core/dataset/type';
|
|
||||||
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
|
import { getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
@@ -110,7 +107,7 @@ const CollectionReader = ({
|
|||||||
|
|
||||||
const formatedDataList = useMemo(
|
const formatedDataList = useMemo(
|
||||||
() =>
|
() =>
|
||||||
datasetDataList.map((item: DatasetCiteItemType) => {
|
datasetDataList.map((item) => {
|
||||||
const isCurrentSelected = currentQuoteItem?.id === item._id;
|
const isCurrentSelected = currentQuoteItem?.id === item._id;
|
||||||
const quoteIndex = filterResults.findIndex((res) => res.id === item._id);
|
const quoteIndex = filterResults.findIndex((res) => res.id === item._id);
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import type { NextApiResponse } from 'next';
|
import type { NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@fastgpt/service/common/response';
|
|
||||||
|
|
||||||
import { pushQuestionGuideUsage } from '@/service/support/wallet/usage/push';
|
import { pushQuestionGuideUsage } from '@/service/support/wallet/usage/push';
|
||||||
import { createQuestionGuide } from '@fastgpt/service/core/ai/functions/createQuestionGuide';
|
import { createQuestionGuide } from '@fastgpt/service/core/ai/functions/createQuestionGuide';
|
||||||
import { type ApiRequestProps } from '@fastgpt/service/type/next';
|
import { type ApiRequestProps } from '@fastgpt/service/type/next';
|
||||||
import { NextAPI } from '@/service/middleware/entry';
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
import { type OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
|
||||||
import { type ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
|
|
||||||
import { type AuthModeType } from '@fastgpt/service/support/permission/type';
|
import { type AuthModeType } from '@fastgpt/service/support/permission/type';
|
||||||
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||||
import { authOutLinkValid } from '@fastgpt/service/support/permission/publish/authLink';
|
import { authOutLinkValid } from '@fastgpt/service/support/permission/publish/authLink';
|
||||||
@@ -17,53 +14,47 @@ import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
|||||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||||
import { getDefaultLLMModel } from '@fastgpt/service/core/ai/model';
|
import { getDefaultLLMModel } from '@fastgpt/service/core/ai/model';
|
||||||
|
import {
|
||||||
|
CreateQuestionGuideBodySchema,
|
||||||
|
CreateQuestionGuideResponseSchema,
|
||||||
|
type CreateQuestionGuideResponseType
|
||||||
|
} from '@fastgpt/global/openapi/core/ai/agent/api';
|
||||||
|
import { type OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||||
|
import { type ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
|
||||||
|
|
||||||
async function handler(
|
async function handler(
|
||||||
req: ApiRequestProps<
|
req: ApiRequestProps,
|
||||||
OutLinkChatAuthProps & {
|
_res: NextApiResponse
|
||||||
messages: ChatCompletionMessageParam[];
|
): Promise<CreateQuestionGuideResponseType> {
|
||||||
}
|
const { messages } = CreateQuestionGuideBodySchema.parse(req.body);
|
||||||
>,
|
|
||||||
res: NextApiResponse<any>
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const { messages } = req.body;
|
|
||||||
|
|
||||||
const { tmbId, teamId } = await authChatCert({
|
const { tmbId, teamId } = await authChatCert({
|
||||||
req,
|
req,
|
||||||
authToken: true,
|
authToken: true,
|
||||||
authApiKey: true
|
authApiKey: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const qgModel = getDefaultLLMModel();
|
const qgModel = getDefaultLLMModel();
|
||||||
|
|
||||||
const { result, inputTokens, outputTokens } = await createQuestionGuide({
|
const { result, inputTokens, outputTokens } = await createQuestionGuide({
|
||||||
messages,
|
messages: messages as ChatCompletionMessageParam[],
|
||||||
model: qgModel.model
|
model: qgModel.model
|
||||||
});
|
});
|
||||||
|
|
||||||
jsonRes(res, {
|
pushQuestionGuideUsage({
|
||||||
data: result
|
model: qgModel.model,
|
||||||
});
|
inputTokens,
|
||||||
|
outputTokens,
|
||||||
|
teamId,
|
||||||
|
tmbId
|
||||||
|
});
|
||||||
|
|
||||||
pushQuestionGuideUsage({
|
return CreateQuestionGuideResponseSchema.parse(result);
|
||||||
model: qgModel.model,
|
|
||||||
inputTokens,
|
|
||||||
outputTokens,
|
|
||||||
teamId,
|
|
||||||
tmbId
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NextAPI(handler);
|
export default NextAPI(handler);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Abandoned
|
Abandoned
|
||||||
Different chat source
|
Different chat source
|
||||||
1. token (header)
|
1. token (header)
|
||||||
|
|||||||
@@ -89,8 +89,7 @@ async function handler(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sort: { createTime: -1 },
|
sort: { createTime: -1 }
|
||||||
session
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||||
import { NextAPI } from '@/service/middleware/entry';
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
import type {
|
import {
|
||||||
GetHelperBotChatRecordsParamsType,
|
GetHelperBotChatRecordsParamsSchema,
|
||||||
GetHelperBotChatRecordsResponseType
|
type GetHelperBotChatRecordsParamsType,
|
||||||
|
type GetHelperBotChatRecordsResponseType
|
||||||
} from '@fastgpt/global/openapi/core/chat/helperBot/api';
|
} from '@fastgpt/global/openapi/core/chat/helperBot/api';
|
||||||
import { authHelperBotChatCrud } from '@/service/support/permission/auth/chat';
|
import { authHelperBotChatCrud } from '@/service/support/permission/auth/chat';
|
||||||
import { MongoHelperBotChatItem } from '../../../../../../../../packages/service/core/chat/HelperBot/chatItemSchema';
|
import { MongoHelperBotChatItem } from '../../../../../../../../packages/service/core/chat/HelperBot/chatItemSchema';
|
||||||
@@ -18,15 +19,15 @@ async function handler(
|
|||||||
req: ApiRequestProps<getRecordsBody, getRecordsQuery>,
|
req: ApiRequestProps<getRecordsBody, getRecordsQuery>,
|
||||||
res: ApiResponseType<any>
|
res: ApiResponseType<any>
|
||||||
): Promise<getRecordsResponse> {
|
): Promise<getRecordsResponse> {
|
||||||
const { type, chatId } = req.query;
|
const { type, chatId } = GetHelperBotChatRecordsParamsSchema.parse(req.query);
|
||||||
const { chat, userId } = await authHelperBotChatCrud({
|
const { userId } = await authHelperBotChatCrud({
|
||||||
type,
|
type,
|
||||||
chatId,
|
chatId,
|
||||||
req,
|
req,
|
||||||
authToken: true
|
authToken: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const { offset, pageSize } = parsePaginationRequest(req);
|
const { offset } = parsePaginationRequest(req);
|
||||||
|
|
||||||
const [histories, total] = await Promise.all([
|
const [histories, total] = await Promise.all([
|
||||||
MongoHelperBotChatItem.find({ userId, chatId }).sort({ _id: -1 }).skip(offset).limit(20).lean(),
|
MongoHelperBotChatItem.find({ userId, chatId }).sort({ _id: -1 }).skip(offset).limit(20).lean(),
|
||||||
|
|||||||
@@ -1,31 +1,29 @@
|
|||||||
import type { NextApiResponse } from 'next';
|
import type { NextApiResponse } from 'next';
|
||||||
import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema';
|
import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema';
|
||||||
import { type PaginationProps, type PaginationResponse } from '@fastgpt/web/common/fetch/type';
|
|
||||||
import { NextAPI } from '@/service/middleware/entry';
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
import { type ApiRequestProps } from '@fastgpt/service/type/next';
|
import { type ApiRequestProps } from '@fastgpt/service/type/next';
|
||||||
import { type ChatInputGuideSchemaType } from '@fastgpt/global/core/chat/inputGuide/type';
|
|
||||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||||
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
|
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
|
||||||
|
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
|
||||||
export type ChatInputGuideProps = PaginationProps<{
|
import {
|
||||||
appId: string;
|
ChatInputGuideListBodySchema,
|
||||||
searchKey: string;
|
ChatInputGuideListResponseSchema,
|
||||||
}>;
|
type ChatInputGuideListResponseType
|
||||||
export type ChatInputGuideResponse = PaginationResponse<ChatInputGuideSchemaType>;
|
} from '@fastgpt/global/openapi/core/chat/inputGuide/api';
|
||||||
|
|
||||||
async function handler(
|
async function handler(
|
||||||
req: ApiRequestProps<ChatInputGuideProps>,
|
req: ApiRequestProps,
|
||||||
res: NextApiResponse<any>
|
res: NextApiResponse<any>
|
||||||
): Promise<ChatInputGuideResponse> {
|
): Promise<ChatInputGuideListResponseType> {
|
||||||
const { appId, searchKey } = req.body;
|
const { appId, searchKey } = ChatInputGuideListBodySchema.parse(req.body);
|
||||||
const { offset, pageSize } = parsePaginationRequest(req);
|
const { offset, pageSize } = parsePaginationRequest(req);
|
||||||
|
|
||||||
await authApp({ req, appId, authToken: true, per: ReadPermissionVal });
|
await authApp({ req, appId, authToken: true, per: ReadPermissionVal });
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
appId,
|
appId,
|
||||||
...(searchKey && { text: { $regex: new RegExp(searchKey, 'i') } })
|
...(searchKey && { text: { $regex: replaceRegChars(searchKey), $options: 'i' } })
|
||||||
};
|
};
|
||||||
|
|
||||||
const [result, total] = await Promise.all([
|
const [result, total] = await Promise.all([
|
||||||
@@ -33,10 +31,10 @@ async function handler(
|
|||||||
MongoChatInputGuide.countDocuments(params)
|
MongoChatInputGuide.countDocuments(params)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return ChatInputGuideListResponseSchema.parse({
|
||||||
list: result,
|
list: result,
|
||||||
total
|
total
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NextAPI(handler);
|
export default NextAPI(handler);
|
||||||
|
|||||||
@@ -1,15 +1,8 @@
|
|||||||
import { NextAPI } from '@/service/middleware/entry';
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
import { authChatCrud, authCollectionInChat } from '@/service/support/permission/auth/chat';
|
import { authChatCrud, authCollectionInChat } from '@/service/support/permission/auth/chat';
|
||||||
import {
|
import { type DatasetDataSchemaType } from '@fastgpt/global/core/dataset/type';
|
||||||
type DatasetCiteItemType,
|
|
||||||
type DatasetDataSchemaType
|
|
||||||
} from '@fastgpt/global/core/dataset/type';
|
|
||||||
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
import { MongoDatasetData } from '@fastgpt/service/core/dataset/data/schema';
|
||||||
import { type ApiRequestProps } from '@fastgpt/service/type/next';
|
import { type ApiRequestProps } from '@fastgpt/service/type/next';
|
||||||
import {
|
|
||||||
type LinkedListResponse,
|
|
||||||
type LinkedPaginationProps
|
|
||||||
} from '@fastgpt/web/common/fetch/type';
|
|
||||||
import { type FilterQuery, Types } from 'mongoose';
|
import { type FilterQuery, Types } from 'mongoose';
|
||||||
import { quoteDataFieldSelector } from '@/service/core/chat/constants';
|
import { quoteDataFieldSelector } from '@/service/core/chat/constants';
|
||||||
import { processChatTimeFilter } from '@/service/core/chat/utils';
|
import { processChatTimeFilter } from '@/service/core/chat/utils';
|
||||||
@@ -17,27 +10,14 @@ import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
|||||||
import { getCollectionWithDataset } from '@fastgpt/service/core/dataset/controller';
|
import { getCollectionWithDataset } from '@fastgpt/service/core/dataset/controller';
|
||||||
import { getFormatDatasetCiteList } from '@fastgpt/service/core/dataset/data/controller';
|
import { getFormatDatasetCiteList } from '@fastgpt/service/core/dataset/data/controller';
|
||||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||||
|
import {
|
||||||
export type GetCollectionQuoteProps = LinkedPaginationProps<{}, number> & {
|
GetCollectionQuoteBodySchema,
|
||||||
chatId: string;
|
type GetCollectionQuoteResType
|
||||||
chatItemDataId: string;
|
} from '@fastgpt/global/openapi/core/chat/quote/api';
|
||||||
|
|
||||||
collectionId: string;
|
|
||||||
|
|
||||||
appId: string;
|
|
||||||
shareId?: string;
|
|
||||||
outLinkUid?: string;
|
|
||||||
teamId?: string;
|
|
||||||
teamToken?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetCollectionQuoteRes = LinkedListResponse<DatasetCiteItemType, number>;
|
|
||||||
|
|
||||||
type BaseMatchType = FilterQuery<DatasetDataSchemaType>;
|
type BaseMatchType = FilterQuery<DatasetDataSchemaType>;
|
||||||
|
|
||||||
async function handler(
|
async function handler(req: ApiRequestProps): Promise<GetCollectionQuoteResType> {
|
||||||
req: ApiRequestProps<GetCollectionQuoteProps>
|
|
||||||
): Promise<GetCollectionQuoteRes> {
|
|
||||||
const {
|
const {
|
||||||
initialId,
|
initialId,
|
||||||
prevId,
|
prevId,
|
||||||
@@ -52,10 +32,10 @@ async function handler(
|
|||||||
outLinkUid,
|
outLinkUid,
|
||||||
teamId,
|
teamId,
|
||||||
teamToken,
|
teamToken,
|
||||||
pageSize = 15
|
pageSize
|
||||||
} = req.body;
|
} = GetCollectionQuoteBodySchema.parse(req.body);
|
||||||
|
|
||||||
const limitedPageSize = Math.min(pageSize, 30);
|
const limitedPageSize = pageSize;
|
||||||
|
|
||||||
const [collection, { chat, showFullText }, chatItem] = await Promise.all([
|
const [collection, { chat, showFullText }, chatItem] = await Promise.all([
|
||||||
getCollectionWithDataset(collectionId),
|
getCollectionWithDataset(collectionId),
|
||||||
@@ -125,7 +105,7 @@ async function handleInitialLoad({
|
|||||||
pageSize: number;
|
pageSize: number;
|
||||||
chatTime: Date;
|
chatTime: Date;
|
||||||
baseMatch: BaseMatchType;
|
baseMatch: BaseMatchType;
|
||||||
}): Promise<GetCollectionQuoteRes> {
|
}): Promise<GetCollectionQuoteResType> {
|
||||||
const centerNode = await MongoDatasetData.findOne(
|
const centerNode = await MongoDatasetData.findOne(
|
||||||
{
|
{
|
||||||
_id: new Types.ObjectId(initialId)
|
_id: new Types.ObjectId(initialId)
|
||||||
@@ -192,7 +172,7 @@ async function handlePaginatedLoad({
|
|||||||
pageSize: number;
|
pageSize: number;
|
||||||
chatTime: Date;
|
chatTime: Date;
|
||||||
baseMatch: BaseMatchType;
|
baseMatch: BaseMatchType;
|
||||||
}): Promise<GetCollectionQuoteRes> {
|
}): Promise<GetCollectionQuoteResType> {
|
||||||
const { list, hasMore } = prevId
|
const { list, hasMore } = prevId
|
||||||
? await getPrevNodes(prevId, nextAnchor, pageSize, baseMatch)
|
? await getPrevNodes(prevId, nextAnchor, pageSize, baseMatch)
|
||||||
: await getNextNodes(nextId!, nextAnchor, pageSize, baseMatch);
|
: await getNextNodes(nextId!, nextAnchor, pageSize, baseMatch);
|
||||||
|
|||||||
@@ -6,38 +6,25 @@ import { quoteDataFieldSelector } from '@/service/core/chat/constants';
|
|||||||
import { processChatTimeFilter } from '@/service/core/chat/utils';
|
import { processChatTimeFilter } from '@/service/core/chat/utils';
|
||||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||||
import { getFormatDatasetCiteList } from '@fastgpt/service/core/dataset/data/controller';
|
import { getFormatDatasetCiteList } from '@fastgpt/service/core/dataset/data/controller';
|
||||||
import type { DatasetCiteItemType } from '@fastgpt/global/core/dataset/type';
|
|
||||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||||
|
import {
|
||||||
|
GetQuoteBodySchema,
|
||||||
|
GetQuoteResponseSchema,
|
||||||
|
type GetQuoteResponseType
|
||||||
|
} from '@fastgpt/global/openapi/core/chat/quote/api';
|
||||||
|
|
||||||
export type GetQuoteProps = {
|
async function handler(req: ApiRequestProps): Promise<GetQuoteResponseType> {
|
||||||
datasetDataIdList: string[];
|
|
||||||
|
|
||||||
collectionIdList: string[];
|
|
||||||
chatId: string;
|
|
||||||
chatItemDataId: string;
|
|
||||||
appId: string;
|
|
||||||
shareId?: string;
|
|
||||||
outLinkUid?: string;
|
|
||||||
teamId?: string;
|
|
||||||
teamToken?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetQuotesRes = DatasetCiteItemType[];
|
|
||||||
|
|
||||||
async function handler(req: ApiRequestProps<GetQuoteProps>): Promise<GetQuotesRes> {
|
|
||||||
const {
|
const {
|
||||||
appId,
|
appId,
|
||||||
chatId,
|
chatId,
|
||||||
chatItemDataId,
|
chatItemDataId,
|
||||||
|
|
||||||
shareId,
|
shareId,
|
||||||
outLinkUid,
|
outLinkUid,
|
||||||
teamId,
|
teamId,
|
||||||
teamToken,
|
teamToken,
|
||||||
|
|
||||||
collectionIdList,
|
collectionIdList,
|
||||||
datasetDataIdList
|
datasetDataIdList
|
||||||
} = req.body;
|
} = GetQuoteBodySchema.parse(req.body);
|
||||||
|
|
||||||
const [{ chat, showCite }, chatItem] = await Promise.all([
|
const [{ chat, showCite }, chatItem] = await Promise.all([
|
||||||
authChatCrud({
|
authChatCrud({
|
||||||
@@ -60,12 +47,10 @@ async function handler(req: ApiRequestProps<GetQuoteProps>): Promise<GetQuotesRe
|
|||||||
quoteDataFieldSelector
|
quoteDataFieldSelector
|
||||||
).lean();
|
).lean();
|
||||||
|
|
||||||
// Get image preview url
|
|
||||||
const formatPreviewUrlList = getFormatDatasetCiteList(list);
|
const formatPreviewUrlList = getFormatDatasetCiteList(list);
|
||||||
|
|
||||||
const quoteList = processChatTimeFilter(formatPreviewUrlList, chatItem.time);
|
const quoteList = processChatTimeFilter(formatPreviewUrlList, chatItem.time);
|
||||||
|
|
||||||
return quoteList;
|
return GetQuoteResponseSchema.parse(quoteList);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NextAPI(handler);
|
export default NextAPI(handler);
|
||||||
|
|||||||
@@ -6,12 +6,6 @@ import { NextAPI } from '@/service/middleware/entry';
|
|||||||
import { type OutLinkSchema } from '@fastgpt/global/support/outLink/type';
|
import { type OutLinkSchema } from '@fastgpt/global/support/outLink/type';
|
||||||
import type { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
|
import type { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
|
||||||
|
|
||||||
export const ApiMetadata = {
|
|
||||||
name: '获取应用内所有 Outlink',
|
|
||||||
author: 'Finley',
|
|
||||||
version: '0.1.0'
|
|
||||||
};
|
|
||||||
|
|
||||||
export type OutLinkListQuery = {
|
export type OutLinkListQuery = {
|
||||||
appId: string; // 应用 ID
|
appId: string; // 应用 ID
|
||||||
type: `${PublishChannelEnum}`;
|
type: `${PublishChannelEnum}`;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const formatIndexes = async ({
|
|||||||
indexPrefix?: string;
|
indexPrefix?: string;
|
||||||
}): Promise<
|
}): Promise<
|
||||||
{
|
{
|
||||||
type: `${DatasetDataIndexTypeEnum}`;
|
type: DatasetDataIndexTypeEnum;
|
||||||
text: string;
|
text: string;
|
||||||
dataId?: string;
|
dataId?: string;
|
||||||
}[]
|
}[]
|
||||||
|
|||||||
@@ -12,12 +12,15 @@ import type {
|
|||||||
getChatRecordsBody,
|
getChatRecordsBody,
|
||||||
getChatRecordsResponse
|
getChatRecordsResponse
|
||||||
} from '@/pages/api/core/chat/record/getRecords_v2';
|
} from '@/pages/api/core/chat/record/getRecords_v2';
|
||||||
import type { GetQuoteProps, GetQuotesRes } from '@/pages/api/core/chat/quote/getQuote';
|
|
||||||
import type {
|
import type {
|
||||||
GetCollectionQuoteProps,
|
GetQuoteBodyType,
|
||||||
GetCollectionQuoteRes
|
GetQuoteResponseType
|
||||||
} from '@/pages/api/core/chat/quote/getCollectionQuote';
|
} from '@fastgpt/global/openapi/core/chat/quote/api';
|
||||||
import type { ChatSettingModelType, ChatSettingType } from '@fastgpt/global/core/chat/setting/type';
|
import type { ChatSettingModelType, ChatSettingType } from '@fastgpt/global/core/chat/setting/type';
|
||||||
|
import type {
|
||||||
|
GetCollectionQuoteBodyType,
|
||||||
|
GetCollectionQuoteResType
|
||||||
|
} from '@fastgpt/global/openapi/core/chat/quote/api';
|
||||||
import type {
|
import type {
|
||||||
GetChatFavouriteListParamsType,
|
GetChatFavouriteListParamsType,
|
||||||
UpdateFavouriteAppParamsType
|
UpdateFavouriteAppParamsType
|
||||||
@@ -57,11 +60,11 @@ export const getChatRecords = (data: getChatRecordsBody) =>
|
|||||||
export const delChatRecordById = (data: DeleteChatItemProps) =>
|
export const delChatRecordById = (data: DeleteChatItemProps) =>
|
||||||
POST(`/core/chat/item/delete`, data);
|
POST(`/core/chat/item/delete`, data);
|
||||||
|
|
||||||
export const getQuoteDataList = (data: GetQuoteProps) =>
|
export const getQuoteDataList = (data: GetQuoteBodyType) =>
|
||||||
POST<GetQuotesRes>(`/core/chat/quote/getQuote`, data);
|
POST<GetQuoteResponseType>(`/core/chat/quote/getQuote`, data);
|
||||||
|
|
||||||
export const getCollectionQuote = (data: GetCollectionQuoteProps) =>
|
export const getCollectionQuote = (data: GetCollectionQuoteBodyType) =>
|
||||||
POST<GetCollectionQuoteRes>(`/core/chat/quote/getCollectionQuote`, data);
|
POST<GetCollectionQuoteResType>(`/core/chat/quote/getCollectionQuote`, data);
|
||||||
|
|
||||||
/*---------- chat setting ------------*/
|
/*---------- chat setting ------------*/
|
||||||
export const getChatSetting = () => GET<ChatSettingType>('/proApi/core/chat/setting/detail');
|
export const getChatSetting = () => GET<ChatSettingType>('/proApi/core/chat/setting/detail');
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
|
import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
|
||||||
import type {
|
import type {
|
||||||
ChatInputGuideProps,
|
ChatInputGuideListBodyType,
|
||||||
ChatInputGuideResponse
|
ChatInputGuideListResponseType
|
||||||
} from '@/pages/api/core/chat/inputGuide/list';
|
} from '@fastgpt/global/openapi/core/chat/inputGuide/api';
|
||||||
import type {
|
import type {
|
||||||
countChatInputGuideTotalQuery,
|
countChatInputGuideTotalQuery,
|
||||||
countChatInputGuideTotalResponse
|
countChatInputGuideTotalResponse
|
||||||
@@ -24,8 +24,8 @@ export const getCountChatInputGuideTotal = (data: countChatInputGuideTotalQuery)
|
|||||||
/**
|
/**
|
||||||
* Get chat input guide list
|
* Get chat input guide list
|
||||||
*/
|
*/
|
||||||
export const getChatInputGuideList = (data: ChatInputGuideProps) =>
|
export const getChatInputGuideList = (data: ChatInputGuideListBodyType) =>
|
||||||
POST<ChatInputGuideResponse>(`/core/chat/inputGuide/list`, data);
|
POST<ChatInputGuideListResponseType>(`/core/chat/inputGuide/list`, data);
|
||||||
|
|
||||||
export const queryChatInputGuideList = (data: QueryChatInputGuideBody, url?: string) => {
|
export const queryChatInputGuideList = (data: QueryChatInputGuideBody, url?: string) => {
|
||||||
if (url) {
|
if (url) {
|
||||||
|
|||||||
Reference in New Issue
Block a user