AI配置增加字段适配处理
This commit is contained in:
@@ -0,0 +1,305 @@
|
||||
# AI配置测试保存功能说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
在 web-admin 的 AI 配置管理页面中,测试接口成功后可以将测试时使用的参数保存到数据库中,包括:
|
||||
- API完整URL
|
||||
- API Token
|
||||
- Bot ID
|
||||
- Workflow ID
|
||||
- 自定义请求头
|
||||
- 自定义请求参数
|
||||
- 是否支持流式输出
|
||||
|
||||
## 使用流程
|
||||
|
||||
### 1. 打开测试对话框
|
||||
在 AI 配置列表中,点击某个配置的"测试"按钮,打开测试对话框。
|
||||
|
||||
### 2. 配置测试参数
|
||||
测试对话框会自动填充当前配置的参数:
|
||||
- **请求URL**: 从配置的 `apiBaseUrl` 字段获取
|
||||
- **请求头**: 自动构建,包含 `Authorization` 和 `Content-Type`
|
||||
- **请求体**: 自动构建,包含 `bot_id`、`user_id`、`stream`、`additional_messages` 等
|
||||
|
||||
### 3. 修改测试参数(可选)
|
||||
您可以根据需要修改:
|
||||
- 请求URL
|
||||
- 请求头中的参数
|
||||
- 请求体中的参数
|
||||
- 测试消息内容
|
||||
- 是否启用流式响应
|
||||
|
||||
### 4. 发送测试请求
|
||||
点击"发送测试请求"或"发送流式测试"按钮,系统会向配置的API发送请求。
|
||||
|
||||
### 5. 查看响应结果
|
||||
右侧会显示:
|
||||
- 状态码
|
||||
- 响应头
|
||||
- 响应体
|
||||
|
||||
### 6. 保存测试配置
|
||||
如果测试成功(状态码为200),会显示"保存测试配置"按钮。点击该按钮,系统会:
|
||||
|
||||
#### 6.1 解析请求头
|
||||
- 提取 `Authorization` 头中的 Token(移除 "Bearer " 前缀)
|
||||
- 将其他自定义请求头保存到 `customHeaders` 字段(JSON格式)
|
||||
|
||||
#### 6.2 解析请求体
|
||||
- 提取 `bot_id` 保存到 `botId` 字段
|
||||
- 提取 `workflow_id` 保存到 `workflowId` 字段
|
||||
- 提取 `stream` 保存到 `supportStream` 字段
|
||||
- 将其他自定义参数保存到 `customParams` 字段(JSON格式)
|
||||
|
||||
#### 6.3 更新配置
|
||||
调用后端接口 `/aiConfig/updateFromTest`,更新配置信息。
|
||||
|
||||
## 后端接口
|
||||
|
||||
### 接口地址
|
||||
```
|
||||
PUT /aiConfig/updateFromTest
|
||||
```
|
||||
|
||||
### 请求参数
|
||||
```json
|
||||
{
|
||||
"id": "配置ID",
|
||||
"apiBaseUrl": "完整的API URL",
|
||||
"apiToken": "API访问令牌",
|
||||
"botId": "Bot ID(Coze专用)",
|
||||
"workflowId": "Workflow ID(Coze专用)",
|
||||
"customHeaders": "自定义请求头(JSON字符串)",
|
||||
"customParams": "自定义参数(JSON字符串)",
|
||||
"supportStream": 1
|
||||
}
|
||||
```
|
||||
|
||||
### 响应示例
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "更新成功",
|
||||
"data": {
|
||||
"id": "1234567890",
|
||||
"configName": "测试配置",
|
||||
"apiBaseUrl": "https://api.coze.cn/v3/chat",
|
||||
"apiToken": "pat_xxx",
|
||||
"botId": "bot_123",
|
||||
"workflowId": "workflow_456",
|
||||
"customHeaders": "{\"X-Custom-Header\":\"value\"}",
|
||||
"customParams": "{\"custom_param\":\"value\"}",
|
||||
"supportStream": 1,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 参数提取规则
|
||||
|
||||
### 1. API Token提取
|
||||
从请求头的 `Authorization` 字段中提取:
|
||||
```json
|
||||
{
|
||||
"Authorization": "Bearer pat_xxx"
|
||||
}
|
||||
```
|
||||
提取结果:`apiToken = "pat_xxx"`
|
||||
|
||||
### 2. Bot ID提取
|
||||
从请求体的 `bot_id` 字段中提取:
|
||||
```json
|
||||
{
|
||||
"bot_id": "bot_123"
|
||||
}
|
||||
```
|
||||
提取结果:`botId = "bot_123"`
|
||||
|
||||
### 3. Workflow ID提取
|
||||
从请求体的 `workflow_id` 字段中提取:
|
||||
```json
|
||||
{
|
||||
"workflow_id": "workflow_456"
|
||||
}
|
||||
```
|
||||
提取结果:`workflowId = "workflow_456"`
|
||||
|
||||
### 4. 流式支持提取
|
||||
从请求体的 `stream` 字段中提取:
|
||||
```json
|
||||
{
|
||||
"stream": true
|
||||
}
|
||||
```
|
||||
提取结果:`supportStream = 1`
|
||||
|
||||
### 5. 自定义请求头
|
||||
移除 `Authorization` 和 `Content-Type` 后的其他请求头:
|
||||
```json
|
||||
{
|
||||
"X-Custom-Header": "value",
|
||||
"X-Another-Header": "value2"
|
||||
}
|
||||
```
|
||||
保存为JSON字符串到 `customHeaders` 字段
|
||||
|
||||
### 6. 自定义参数
|
||||
移除 `bot_id`、`workflow_id`、`stream`、`user_id`、`additional_messages` 后的其他参数:
|
||||
```json
|
||||
{
|
||||
"custom_param": "value",
|
||||
"another_param": "value2"
|
||||
}
|
||||
```
|
||||
保存为JSON字符串到 `customParams` 字段
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 示例1:标准Coze配置
|
||||
|
||||
**测试前配置**:
|
||||
```json
|
||||
{
|
||||
"apiBaseUrl": "https://api.coze.cn",
|
||||
"apiToken": "old_token",
|
||||
"botId": "old_bot_id"
|
||||
}
|
||||
```
|
||||
|
||||
**测试请求**:
|
||||
- URL: `https://api.coze.cn/v3/chat`
|
||||
- 请求头:
|
||||
```json
|
||||
{
|
||||
"Authorization": "Bearer new_token",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
```
|
||||
- 请求体:
|
||||
```json
|
||||
{
|
||||
"bot_id": "new_bot_id",
|
||||
"user_id": "test_user",
|
||||
"stream": false,
|
||||
"additional_messages": [...]
|
||||
}
|
||||
```
|
||||
|
||||
**保存后配置**:
|
||||
```json
|
||||
{
|
||||
"apiBaseUrl": "https://api.coze.cn/v3/chat",
|
||||
"apiToken": "new_token",
|
||||
"botId": "new_bot_id",
|
||||
"supportStream": 0,
|
||||
"customHeaders": "",
|
||||
"customParams": ""
|
||||
}
|
||||
```
|
||||
|
||||
### 示例2:带自定义参数的配置
|
||||
|
||||
**测试请求**:
|
||||
- URL: `https://api.coze.cn/v3/chat`
|
||||
- 请求头:
|
||||
```json
|
||||
{
|
||||
"Authorization": "Bearer token_123",
|
||||
"Content-Type": "application/json",
|
||||
"X-Custom-Header": "custom_value"
|
||||
}
|
||||
```
|
||||
- 请求体:
|
||||
```json
|
||||
{
|
||||
"bot_id": "bot_456",
|
||||
"workflow_id": "workflow_789",
|
||||
"stream": true,
|
||||
"user_id": "test_user",
|
||||
"additional_messages": [...],
|
||||
"custom_param": "param_value"
|
||||
}
|
||||
```
|
||||
|
||||
**保存后配置**:
|
||||
```json
|
||||
{
|
||||
"apiBaseUrl": "https://api.coze.cn/v3/chat",
|
||||
"apiToken": "token_123",
|
||||
"botId": "bot_456",
|
||||
"workflowId": "workflow_789",
|
||||
"supportStream": 1,
|
||||
"customHeaders": "{\"X-Custom-Header\":\"custom_value\"}",
|
||||
"customParams": "{\"custom_param\":\"param_value\"}"
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **测试成功才能保存**: 只有当测试请求返回状态码200时,才会显示"保存测试配置"按钮
|
||||
2. **JSON格式验证**: 保存前会验证请求头和请求体是否为有效的JSON格式
|
||||
3. **自动刷新**: 保存成功后会自动刷新配置列表
|
||||
4. **字段覆盖**: 保存时只更新提供的字段,未提供的字段保持原值
|
||||
5. **Token安全**: API Token会完整保存到数据库,请确保数据库安全
|
||||
|
||||
## 错误处理
|
||||
|
||||
### 1. 请求头格式错误
|
||||
```
|
||||
错误信息:请求头格式错误,无法解析
|
||||
原因:请求头不是有效的JSON格式
|
||||
解决:检查请求头的JSON格式是否正确
|
||||
```
|
||||
|
||||
### 2. 请求体格式错误
|
||||
```
|
||||
错误信息:请求体格式错误,无法解析
|
||||
原因:请求体不是有效的JSON格式
|
||||
解决:检查请求体的JSON格式是否正确
|
||||
```
|
||||
|
||||
### 3. 配置不存在
|
||||
```
|
||||
错误信息:测试配置不存在
|
||||
原因:testConfig为空
|
||||
解决:重新打开测试对话框
|
||||
```
|
||||
|
||||
### 4. 保存失败
|
||||
```
|
||||
错误信息:保存失败: [具体错误信息]
|
||||
原因:后端接口调用失败
|
||||
解决:检查网络连接和后端服务状态
|
||||
```
|
||||
|
||||
## 开发说明
|
||||
|
||||
### 前端文件
|
||||
- **页面**: `web-admin/src/views/aiconfig/AiConfigList.vue`
|
||||
- **API**: `web-admin/src/api/aiconfig.ts`
|
||||
- **类型**: `web-admin/src/types/aiconfig.ts`
|
||||
|
||||
### 后端文件
|
||||
- **Controller**: `backend-single/src/main/java/com/emotion/controller/AiConfigController.java`
|
||||
- **Service**: `backend-single/src/main/java/com/emotion/service/AiConfigService.java`
|
||||
- **ServiceImpl**: `backend-single/src/main/java/com/emotion/service/impl/AiConfigServiceImpl.java`
|
||||
- **Request DTO**: `backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigTestUpdateRequest.java`
|
||||
|
||||
### 数据库字段
|
||||
- `api_base_url`: API完整URL
|
||||
- `api_token`: API访问令牌
|
||||
- `bot_id`: Bot ID
|
||||
- `workflow_id`: Workflow ID
|
||||
- `custom_headers`: 自定义请求头(JSON字符串)
|
||||
- `custom_params`: 自定义参数(JSON字符串)
|
||||
- `support_stream`: 是否支持流式输出(0-不支持,1-支持)
|
||||
|
||||
## 更新日志
|
||||
|
||||
### 2025-12-22
|
||||
- 新增测试后保存配置功能
|
||||
- 新增 `AiConfigTestUpdateRequest` DTO
|
||||
- 新增 `updateFromTestRequest` 服务方法
|
||||
- 新增 `/aiConfig/updateFromTest` 接口
|
||||
- 前端新增"保存测试配置"按钮和处理逻辑
|
||||
@@ -204,4 +204,14 @@ export function countByProvider(provider: string) {
|
||||
method: 'get',
|
||||
params: { provider }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 测试后更新AI配置
|
||||
export function updateAiConfigFromTest(data: any) {
|
||||
return request({
|
||||
url: '/aiConfig/updateFromTest',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ export interface AiConfig {
|
||||
apiBaseUrl: string
|
||||
apiToken: string
|
||||
apiVersion?: string
|
||||
clientId?: string
|
||||
clientSecret?: string
|
||||
grantType?: string
|
||||
modelName?: string
|
||||
botId?: string
|
||||
workflowId?: string
|
||||
@@ -66,6 +69,9 @@ export interface AiConfigCreateRequest {
|
||||
apiBaseUrl: string
|
||||
apiToken: string
|
||||
apiVersion?: string
|
||||
clientId?: string
|
||||
clientSecret?: string
|
||||
grantType?: string
|
||||
modelName?: string
|
||||
botId?: string
|
||||
workflowId?: string
|
||||
@@ -108,6 +114,9 @@ export interface AiConfigUpdateRequest {
|
||||
apiBaseUrl?: string
|
||||
apiToken?: string
|
||||
apiVersion?: string
|
||||
clientId?: string
|
||||
clientSecret?: string
|
||||
grantType?: string
|
||||
modelName?: string
|
||||
botId?: string
|
||||
workflowId?: string
|
||||
|
||||
@@ -288,6 +288,34 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="Client ID">
|
||||
<el-input v-model="formData.clientId" placeholder="OAuth客户端ID" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="Client Secret">
|
||||
<el-input
|
||||
v-model="formData.clientSecret"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="OAuth客户端密钥"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="Grant Type">
|
||||
<el-select v-model="formData.grantType" placeholder="授权类型" clearable style="width: 100%">
|
||||
<el-option label="client_credentials" value="client_credentials" />
|
||||
<el-option label="authorization_code" value="authorization_code" />
|
||||
<el-option label="password" value="password" />
|
||||
<el-option label="refresh_token" value="refresh_token" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" v-if="formData.configType === 'coze'">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="Bot ID">
|
||||
@@ -514,6 +542,9 @@
|
||||
<el-descriptions-item label="环境">{{ getEnvironmentLabel(viewData.environment || '') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="API完整URL">{{ viewData.apiBaseUrl }}</el-descriptions-item>
|
||||
<el-descriptions-item label="API令牌">{{ viewData.apiToken }}</el-descriptions-item>
|
||||
<el-descriptions-item label="Client ID">{{ viewData.clientId || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="Client Secret">{{ viewData.clientSecret ? '******' : '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="Grant Type">{{ viewData.grantType || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="模型名称">{{ viewData.modelName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="优先级">{{ viewData.priority || 0 }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
@@ -634,6 +665,13 @@
|
||||
<el-form-item>
|
||||
<el-button @click="handleFormatResponse">格式化响应</el-button>
|
||||
<el-button @click="handleCopyResponse">复制响应</el-button>
|
||||
<el-button
|
||||
v-if="testResponse.status === 200"
|
||||
type="success"
|
||||
@click="handleSaveTestConfig"
|
||||
>
|
||||
保存测试配置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
@@ -659,7 +697,8 @@ import {
|
||||
unsetDefaultConfig,
|
||||
countEnabledConfigs,
|
||||
countDisabledConfigs,
|
||||
countDefaultConfigs
|
||||
countDefaultConfigs,
|
||||
updateAiConfigFromTest
|
||||
} from '@/api/aiconfig'
|
||||
import type { AiConfig, AiConfigPageRequest } from '@/types/aiconfig'
|
||||
import {
|
||||
@@ -711,6 +750,9 @@ const formData = reactive({
|
||||
apiBaseUrl: '',
|
||||
apiToken: '',
|
||||
apiVersion: '',
|
||||
clientId: '',
|
||||
clientSecret: '',
|
||||
grantType: '',
|
||||
modelName: '',
|
||||
botId: '',
|
||||
workflowId: '',
|
||||
@@ -1045,6 +1087,9 @@ const handleDialogClose = () => {
|
||||
apiBaseUrl: '',
|
||||
apiToken: '',
|
||||
apiVersion: '',
|
||||
clientId: '',
|
||||
clientSecret: '',
|
||||
grantType: '',
|
||||
modelName: '',
|
||||
botId: '',
|
||||
workflowId: '',
|
||||
@@ -1411,6 +1456,83 @@ const handleCopyResponse = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 保存测试配置
|
||||
const handleSaveTestConfig = async () => {
|
||||
if (!testConfig.value) {
|
||||
ElMessage.error('测试配置不存在')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 解析请求头
|
||||
let headers: any = {}
|
||||
try {
|
||||
headers = JSON.parse(testRequest.headers)
|
||||
} catch (e) {
|
||||
ElMessage.error('请求头格式错误,无法解析')
|
||||
return
|
||||
}
|
||||
|
||||
// 解析请求体
|
||||
let body: any = {}
|
||||
try {
|
||||
body = JSON.parse(testRequest.body)
|
||||
} catch (e) {
|
||||
ElMessage.error('请求体格式错误,无法解析')
|
||||
return
|
||||
}
|
||||
|
||||
// 提取API Token(从Authorization头中)
|
||||
let apiToken = testConfig.value.apiToken
|
||||
if (headers.Authorization) {
|
||||
// 移除 "Bearer " 前缀
|
||||
apiToken = headers.Authorization.replace(/^Bearer\s+/i, '')
|
||||
delete headers.Authorization // 从自定义头中移除,因为已经保存到apiToken字段
|
||||
}
|
||||
|
||||
// 提取Bot ID和Workflow ID(从请求体中)
|
||||
const botId = body.bot_id || testConfig.value.botId
|
||||
const workflowId = body.workflow_id || testConfig.value.workflowId
|
||||
const supportStream = body.stream !== undefined ? (body.stream ? 1 : 0) : testConfig.value.supportStream
|
||||
|
||||
// 移除已经提取的字段,剩余的作为自定义参数
|
||||
const customParamsBody = { ...body }
|
||||
delete customParamsBody.bot_id
|
||||
delete customParamsBody.workflow_id
|
||||
delete customParamsBody.stream
|
||||
delete customParamsBody.user_id
|
||||
delete customParamsBody.additional_messages
|
||||
|
||||
// 构建更新请求
|
||||
const updateData = {
|
||||
id: testConfig.value.id,
|
||||
apiBaseUrl: testRequest.url,
|
||||
apiToken: apiToken,
|
||||
botId: botId,
|
||||
workflowId: workflowId,
|
||||
customHeaders: Object.keys(headers).length > 0 ? JSON.stringify(headers) : '',
|
||||
customParams: Object.keys(customParamsBody).length > 0 ? JSON.stringify(customParamsBody) : '',
|
||||
supportStream: supportStream
|
||||
}
|
||||
|
||||
// 调用更新接口
|
||||
const res = await updateAiConfigFromTest(updateData)
|
||||
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('测试配置已保存')
|
||||
// 更新testConfig
|
||||
testConfig.value = res.data
|
||||
// 刷新列表
|
||||
await fetchData()
|
||||
} else {
|
||||
ElMessage.error(res.message || '保存失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('保存测试配置失败:', error)
|
||||
ElMessage.error('保存失败: ' + (error.message || error))
|
||||
}
|
||||
}
|
||||
|
||||
// 重置测试
|
||||
const handleResetTest = () => {
|
||||
if (testConfig.value) {
|
||||
|
||||
Reference in New Issue
Block a user