feat: support coze bot & update comments & fix some ui bug.
This commit is contained in:
parent
dc4124e362
commit
f028bea0c5
@ -41,7 +41,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"prettier/prettier": ["warn", { "trailingComma": "es5", "printWidth": 200 }],
|
"prettier/prettier": ["warn", { "trailingComma": "es5", "printWidth": 100 }],
|
||||||
"linebreak-style": "off",
|
"linebreak-style": "off",
|
||||||
"no-console": ["warn", { "allow": ["warn", "error", "log"] }],
|
"no-console": ["warn", { "allow": ["warn", "error", "log"] }],
|
||||||
"no-case-declarations": 0,
|
"no-case-declarations": 0,
|
||||||
|
|||||||
30
README.md
30
README.md
@ -8,11 +8,11 @@
|
|||||||
## 【必看】环境准备
|
## 【必看】环境准备
|
||||||
- **Node 版本: 16.0+**
|
- **Node 版本: 16.0+**
|
||||||
1. 需要准备两个 Terminal,分别启动服务端、前端页面。
|
1. 需要准备两个 Terminal,分别启动服务端、前端页面。
|
||||||
2. 开通 ASR、TTS、LLM、RTC 等服务,可通过 [无代码跑通实时对话式](https://console.volcengine.com/rtc/guide) 快速开通服务, 点击 **快速开始** 中的 **跑通 Demo** 进行服务开通。
|
2. 开通 ASR、TTS、LLM、RTC 等服务,可参考 [开通服务](https://www.volcengine.com/docs/6348/1315561?s=g) 进行相关服务的授权与开通。
|
||||||
3. **根据你自定义的
|
3. **根据你自定义的
|
||||||
RoomId、UserId 以及申请的 AppID、BusinessID(如有)、Token、ASR AppID、TTS AppID,修改 `src/config/config.ts` 文件中 `ConfigFactory` 中 `BaseConfig` 的配置信息**。
|
RoomId、UserId 以及申请的 AppID、BusinessID(如有)、Token、ASR AppID、TTS AppID,修改 `src/config/config.ts` 文件中 `ConfigFactory` 中 `BaseConfig` 的配置信息**。
|
||||||
4. 使用火山引擎控制台账号的 [AK、SK](https://console.volcengine.com/iam/keymanage?s=g)、[SessionToken](https://www.volcengine.com/docs/6348/1315561#sub?s=g)(临时token, 子账号才需要), 修改 `Server/app.js` 文件中的 `ACCOUNT_INFO`。
|
4. 使用火山引擎控制台账号的 [AK、SK](https://console.volcengine.com/iam/keymanage?s=g), 修改 `Server/app.js` 文件中的 `ACCOUNT_INFO`。
|
||||||
5. 您需要在 [火山方舟-在线推理](https://console.volcengine.com/ark/region:ark+cn-beijing/endpoint?config=%7B%7D&s=g) 中创建接入点, 并将模型对应的接入点 ID 填入 `src/config/common.ts` 文件中的 `ARK_V3_MODEL_ID`, 否则无法正常启动智能体。
|
5. 若您使用的是官方模型, 需要在 [火山方舟-在线推理](https://console.volcengine.com/ark/region:ark+cn-beijing/endpoint?config=%7B%7D&s=g) 中创建接入点, 并将模型对应的接入点 ID 填入 `src/config/common.ts` 文件中的 `ARK_V3_MODEL_ID`, 否则无法正常启动智能体。
|
||||||
6. 如果您已经自行完成了服务端的逻辑,可以不依赖 Demo 中的 Server,直接修改前端代码文件 `src/config/index.ts` 中的 `AIGC_PROXY_HOST` 请求域名和接口,并在 `src/app/api.ts` 中修改接口的参数配置 `APIS_CONFIG`。
|
6. 如果您已经自行完成了服务端的逻辑,可以不依赖 Demo 中的 Server,直接修改前端代码文件 `src/config/index.ts` 中的 `AIGC_PROXY_HOST` 请求域名和接口,并在 `src/app/api.ts` 中修改接口的参数配置 `APIS_CONFIG`。
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
@ -43,12 +43,12 @@ yarn dev
|
|||||||
### 常见问题
|
### 常见问题
|
||||||
| 问题 | 解决方案 |
|
| 问题 | 解决方案 |
|
||||||
| :-- | :-- |
|
| :-- | :-- |
|
||||||
| **启动智能体之后, 对话无反馈,或者一直停留在 "AI 准备中, 请稍侯"** | <li>可能因为控制台中相关权限没有正常授予,请参考[流程](https://www.volcengine.com/docs/6348/1315561?s=g)再次确认下是否完成相关操作。此问题的可能性较大,建议仔细对照是否已经将相应的权限开通。</li><li>参数传递可能有问题, 例如参数大小写、类型等问题,请再次确认下这类型问题是否存在。</li><li>相关资源可能未开通或者用量不足,请再次确认。</li><li>**请检查当前使用的模型 ID 等内容都是正确且可用的。**</li> |
|
| 如何使用第三方模型、Coze Bot | 点击页面上的 "修改 AI 设定" 进入配置页,可切换 官方模型/Coze/第三方模型,填写对应参数即可,相关代码对应 `src/components/AISettings/index.tsx` 文件。 |
|
||||||
| `Server/app.js` 中的 `sessionToken` 是什么,该怎么填,为什么要填 | `sessionToken` 是火山引擎子账号发起 OpenAPI 请求时所必须携带的临时 Token,获取方式可参考 [此文章末尾](https://www.volcengine.com/docs/6348/1315561?s=g)。 |
|
| **启动智能体之后, 对话无反馈,或者一直停留在 "AI 准备中, 请稍侯"** | <li>可能因为控制台中相关权限没有正常授予,请参考[流程](https://www.volcengine.com/docs/6348/1315561?s=g)再次确认下是否完成相关操作。此问题的可能性较大,建议仔细对照是否已经将相应的权限开通。</li><li>参数传递可能有问题, 例如参数大小写、类型等问题,请再次确认下这类型问题是否存在。</li><li>相关资源可能未开通或者用量不足/欠费,请再次确认。</li><li>**请检查当前使用的模型 ID 等内容都是正确且可用的。**</li> |
|
||||||
| **浏览器报了 `Uncaught (in promise) r: token_error` 错误** | 请检查您填在项目中的 RTC Token 是否合法,检测用于生成 Token 的 UserId、RoomId 是否与项目中填写的一致;或者 Token 可能过期, 可尝试重新生成下。 |
|
| **浏览器报了 `Uncaught (in promise) r: token_error` 错误** | 请检查您填在项目中的 RTC Token 是否合法,检测用于生成 Token 的 UserId、RoomId 以及 Token 本身是否与项目中填写的一致;或者 Token 可能过期, 可尝试重新生成下。 |
|
||||||
| **[StartVoiceChat]Failed(Reason: The task has been started. Please do not call the startup task interface repeatedly.)** 报错 | 由于目前设置的 RoomId、UserId 为固定值,重复调用 startAudioBot 会导致出错,只需先调用 stopAudioBot 后再重新 startAudioBot 即可。 |
|
| **[StartVoiceChat]Failed(Reason: The task has been started. Please do not call the startup task interface repeatedly.)** 报错 | 由于目前设置的 RoomId、UserId 为固定值,重复调用 startAudioBot 会导致出错,只需先调用 stopAudioBot 后再重新 startAudioBot 即可。 |
|
||||||
| 为什么我的麦克风正常、摄像头也正常,但是设备没有正常工作? | 可能是设备权限未授予,详情可参考 [Web 排查设备权限获取失败问题](https://www.volcengine.com/docs/6348/1356355?s=g)。 |
|
| 为什么我的麦克风正常、摄像头也正常,但是设备没有正常工作? | 可能是设备权限未授予,详情可参考 [Web 排查设备权限获取失败问题](https://www.volcengine.com/docs/6348/1356355?s=g)。 |
|
||||||
| 接口调用时, 返回 "Invalid 'Authorization' header, Pls check your authorization header" 错误 | `Server/app.js` 中的 AK/SK/SessionToken 不正确 |
|
| 接口调用时, 返回 "Invalid 'Authorization' header, Pls check your authorization header" 错误 | `Server/app.js` 中的 AK/SK 不正确 |
|
||||||
| 什么是 RTC | **R**eal **T**ime **C**ommunication, RTC 的概念可参考[官网文档](https://www.volcengine.com/docs/6348/66812?s=g)。 |
|
| 什么是 RTC | **R**eal **T**ime **C**ommunication, RTC 的概念可参考[官网文档](https://www.volcengine.com/docs/6348/66812?s=g)。 |
|
||||||
| 不清楚什么是主账号,什么是子账号 | 可以参考[官方概念](https://www.volcengine.com/docs/6257/64963?hyperlink_open_type=lark.open_in_browser&s=g) 。|
|
| 不清楚什么是主账号,什么是子账号 | 可以参考[官方概念](https://www.volcengine.com/docs/6257/64963?hyperlink_open_type=lark.open_in_browser&s=g) 。|
|
||||||
|
|
||||||
@ -61,7 +61,21 @@ yarn dev
|
|||||||
|
|
||||||
## 更新日志
|
## 更新日志
|
||||||
|
|
||||||
### [1.5.0] - [2025-03-31]
|
### OpenAPI 更新
|
||||||
|
参考 [OpenAPI 更新](https://www.volcengine.com/docs/6348/116363?s=g) 中与 实时对话式 AI 相关的更新内容。
|
||||||
|
|
||||||
|
### Demo 更新
|
||||||
|
#### [1.6.0] - [2025-04-16]
|
||||||
|
- 支持 Coze Bot
|
||||||
|
- 更新部分注释和文档内容
|
||||||
|
- 删除子账号的 SessionToken 配置, 子账号调用无须 SessionToken
|
||||||
|
- 修复通话前修改内容,在通话后配置消失的问题
|
||||||
|
|
||||||
|
#### [1.5.1] - [2025-04-11]
|
||||||
|
- 移除无用代码和依赖
|
||||||
|
- 修复字幕逻辑
|
||||||
|
|
||||||
|
#### [1.5.0] - [2025-03-31]
|
||||||
- 修复部分 UI 问题
|
- 修复部分 UI 问题
|
||||||
- 追加屏幕共享能力 (视觉模型可用,**读屏助手** 人设下可使用)
|
- 追加屏幕共享能力 (视觉模型可用,**读屏助手** 人设下可使用)
|
||||||
- 修改字幕逻辑,避免字幕回调中标点符号、大小写不一致引起的字幕重复问题
|
- 修改字幕逻辑,避免字幕回调中标点符号、大小写不一致引起的字幕重复问题
|
||||||
|
|||||||
@ -27,11 +27,6 @@ const ACCOUNT_INFO = {
|
|||||||
* @notes 必填, 在 https://console.volcengine.com/iam/keymanage/ 获取
|
* @notes 必填, 在 https://console.volcengine.com/iam/keymanage/ 获取
|
||||||
*/
|
*/
|
||||||
secretKey: 'Your SK',
|
secretKey: 'Your SK',
|
||||||
/**
|
|
||||||
* @notes 非必填, 主账号无须传入, 子账号须传, 获取方式可参考
|
|
||||||
* https://www.volcengine.com/docs/6348/1315561 中的 步骤 4-使用子账号调用智能体接口 一节
|
|
||||||
*/
|
|
||||||
// sessionToken: 'Your SessionToken',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.use(bodyParser());
|
app.use(bodyParser());
|
||||||
@ -67,9 +62,7 @@ app.use(async ctx => {
|
|||||||
/** 参考 https://www.volcengine.com/docs/6348/69828 可获取更多 OpenAPI 的信息 */
|
/** 参考 https://www.volcengine.com/docs/6348/69828 可获取更多 OpenAPI 的信息 */
|
||||||
const result = await fetch(`https://rtc.volcengineapi.com?Action=${Action}&Version=${Version}`, {
|
const result = await fetch(`https://rtc.volcengineapi.com?Action=${Action}&Version=${Version}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: openApiRequestData.headers,
|
||||||
...openApiRequestData.headers,
|
|
||||||
},
|
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
});
|
});
|
||||||
const volcResponse = await result.json();
|
const volcResponse = await result.json();
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "aigc",
|
"name": "aigc",
|
||||||
"version": "1.5.0",
|
"version": "1.6.0",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@ -91,6 +91,12 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 36px;
|
gap: 36px;
|
||||||
|
|
||||||
|
.ai-settings-radio {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
.anchor {
|
.anchor {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border-bottom: 12px solid white;
|
border-bottom: 12px solid white;
|
||||||
@ -104,6 +110,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
margin-top: -16px;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
|
|
||||||
.ai-settings-wrapper {
|
.ai-settings-wrapper {
|
||||||
|
|||||||
@ -3,10 +3,10 @@
|
|||||||
* SPDX-license-identifier: BSD-3-Clause
|
* SPDX-license-identifier: BSD-3-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Button, Drawer, Input, Message } from '@arco-design/web-react';
|
import { Button, Drawer, Input, Message, Radio, Tooltip } from '@arco-design/web-react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { IconSwap } from '@arco-design/web-react/icon';
|
import { IconExclamationCircle } from '@arco-design/web-react/icon';
|
||||||
import { StreamIndex } from '@volcengine/rtc';
|
import { StreamIndex } from '@volcengine/rtc';
|
||||||
import CheckIcon from '../CheckIcon';
|
import CheckIcon from '../CheckIcon';
|
||||||
import Config, {
|
import Config, {
|
||||||
@ -18,7 +18,7 @@ import Config, {
|
|||||||
Voice,
|
Voice,
|
||||||
Model,
|
Model,
|
||||||
AI_MODEL,
|
AI_MODEL,
|
||||||
ModelSourceType,
|
MODEL_MODE,
|
||||||
VOICE_INFO_MAP,
|
VOICE_INFO_MAP,
|
||||||
VOICE_TYPE,
|
VOICE_TYPE,
|
||||||
isVisionMode,
|
isVisionMode,
|
||||||
@ -26,7 +26,7 @@ import Config, {
|
|||||||
import TitleCard from '../TitleCard';
|
import TitleCard from '../TitleCard';
|
||||||
import CheckBoxSelector from '@/components/CheckBoxSelector';
|
import CheckBoxSelector from '@/components/CheckBoxSelector';
|
||||||
import RtcClient from '@/lib/RtcClient';
|
import RtcClient from '@/lib/RtcClient';
|
||||||
import { clearHistoryMsg, updateAIConfig, updateScene } from '@/store/slices/room';
|
import { clearHistoryMsg, updateAIConfig, updateModelMode, updateScene } from '@/store/slices/room';
|
||||||
import { RootState } from '@/store';
|
import { RootState } from '@/store';
|
||||||
import utils from '@/utils/utils';
|
import utils from '@/utils/utils';
|
||||||
import { useDeviceState } from '@/lib/useCommon';
|
import { useDeviceState } from '@/lib/useCommon';
|
||||||
@ -42,6 +42,8 @@ export interface IAISettingsProps {
|
|||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RadioGroup = Radio.Group;
|
||||||
|
|
||||||
const SCENES = [
|
const SCENES = [
|
||||||
SCENE.INTELLIGENT_ASSISTANT,
|
SCENE.INTELLIGENT_ASSISTANT,
|
||||||
SCENE.SCREEN_READER,
|
SCENE.SCREEN_READER,
|
||||||
@ -59,17 +61,19 @@ function AISettings({ open, onCancel, onOk }: IAISettingsProps) {
|
|||||||
useDeviceState();
|
useDeviceState();
|
||||||
const room = useSelector((state: RootState) => state.room);
|
const room = useSelector((state: RootState) => state.room);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [use3Part, setUse3Part] = useState(false);
|
const [modelMode, setModelMode] = useState<MODEL_MODE>(room.modelMode);
|
||||||
const [scene, setScene] = useState(room.scene);
|
const [scene, setScene] = useState(room.scene);
|
||||||
const [data, setData] = useState({
|
const [data, setData] = useState({
|
||||||
prompt: Prompt[scene],
|
prompt: Config.Prompt || Prompt[scene],
|
||||||
welcome: Welcome[scene],
|
welcome: Config.WelcomeSpeech || Welcome[scene],
|
||||||
voice: Voice[scene],
|
voice: Config.VoiceType || Voice[scene],
|
||||||
model: Model[scene],
|
model: Config.Model || Model[scene],
|
||||||
|
|
||||||
Url: '',
|
Url: Config.Url || '',
|
||||||
APIKey: '',
|
APIKey: Config.APIKey || '',
|
||||||
customModelName: '',
|
customModelName: (Config.Model || '') as string,
|
||||||
|
|
||||||
|
BotID: Config.BotID || '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleVoiceTypeChanged = (key: string) => {
|
const handleVoiceTypeChanged = (key: string) => {
|
||||||
@ -90,35 +94,55 @@ function AISettings({ open, onCancel, onOk }: IAISettingsProps) {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUseThirdPart = () => {
|
const handleUseThirdPart = (val: MODEL_MODE) => {
|
||||||
setUse3Part(!use3Part);
|
setModelMode(val);
|
||||||
Config.ModeSourceType = use3Part ? ModelSourceType.Custom : ModelSourceType.Available;
|
Config.ModeSourceType = val;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpdateConfig = async () => {
|
const handleUpdateConfig = async () => {
|
||||||
dispatch(updateScene({ scene }));
|
dispatch(updateScene({ scene }));
|
||||||
if (use3Part) {
|
Config.ModeSourceType = modelMode;
|
||||||
if (!data.Url) {
|
switch (modelMode) {
|
||||||
Message.error('请输入正确的第三方模型地址');
|
case MODEL_MODE.ORIGINAL:
|
||||||
return;
|
Config.Url = undefined;
|
||||||
}
|
Config.APIKey = undefined;
|
||||||
if (!data.Url.startsWith('http://') && !data.Url.startsWith('https://')) {
|
break;
|
||||||
Message.error('第三方模型请求地址格式不正确, 请以 http:// 或 https:// 为开头');
|
case MODEL_MODE.COZE:
|
||||||
return;
|
if (!data.APIKey) {
|
||||||
}
|
Message.error('访问令牌必填');
|
||||||
Config.Url = data.Url;
|
return;
|
||||||
Config.APIKey = data.APIKey;
|
}
|
||||||
Config.ModeSourceType = ModelSourceType.Custom;
|
if (!data.BotID) {
|
||||||
} else {
|
Message.error('智能体 ID 必填');
|
||||||
Config.Url = undefined;
|
return;
|
||||||
Config.APIKey = undefined;
|
}
|
||||||
Config.ModeSourceType = ModelSourceType.Available;
|
Config.APIKey = data.APIKey;
|
||||||
|
Config.BotID = data.BotID;
|
||||||
|
break;
|
||||||
|
case MODEL_MODE.VENDOR:
|
||||||
|
if (!data.Url) {
|
||||||
|
Message.error('请输入正确的第三方模型地址');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!data.Url.startsWith('http://') && !data.Url.startsWith('https://')) {
|
||||||
|
Message.error('第三方模型请求地址格式不正确, 请以 http:// 或 https:// 为开头');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Config.Url = data.Url;
|
||||||
|
Config.APIKey = data.APIKey;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
Config.Model = use3Part ? (data.customModelName as AI_MODEL) : (data.model as AI_MODEL);
|
Config.Model =
|
||||||
|
modelMode === MODEL_MODE.VENDOR
|
||||||
|
? (data.customModelName as AI_MODEL)
|
||||||
|
: (data.model as AI_MODEL);
|
||||||
Config.Prompt = data.prompt;
|
Config.Prompt = data.prompt;
|
||||||
Config.VoiceType = data.voice;
|
Config.VoiceType = data.voice;
|
||||||
Config.WelcomeSpeech = data.welcome;
|
Config.WelcomeSpeech = data.welcome;
|
||||||
|
dispatch(updateModelMode(modelMode));
|
||||||
dispatch(updateAIConfig(Config.aigcConfig));
|
dispatch(updateAIConfig(Config.aigcConfig));
|
||||||
|
|
||||||
if (isVisionMode(data.model)) {
|
if (isVisionMode(data.model)) {
|
||||||
@ -218,32 +242,85 @@ function AISettings({ open, onCancel, onOk }: IAISettingsProps) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<TitleCard title="Prompt">
|
<RadioGroup
|
||||||
<Input.TextArea
|
options={[
|
||||||
autoSize
|
{
|
||||||
value={data.prompt}
|
value: MODEL_MODE.ORIGINAL,
|
||||||
onChange={(val) => {
|
label: '官方模型',
|
||||||
setData((prev) => ({
|
},
|
||||||
...prev,
|
{
|
||||||
prompt: val,
|
value: MODEL_MODE.COZE,
|
||||||
}));
|
label: (
|
||||||
}}
|
<div className={styles['radio-text']}>
|
||||||
placeholder="请输入你需要的 Prompt 设定"
|
<span style={{ marginRight: '4px' }}>Coze</span>
|
||||||
/>
|
<Tooltip
|
||||||
</TitleCard>
|
content={
|
||||||
<TitleCard title="欢迎语">
|
<div>
|
||||||
<Input.TextArea
|
访问令牌可参考{' '}
|
||||||
autoSize
|
<a
|
||||||
value={data.welcome}
|
href="https://www.coze.cn/open/docs/developer_guides/pat"
|
||||||
onChange={(val) => {
|
target="_blank"
|
||||||
setData((prev) => ({
|
rel="noreferrer"
|
||||||
...prev,
|
style={{ color: 'gray' }}
|
||||||
welcome: val,
|
>
|
||||||
}));
|
添加个人访问令牌
|
||||||
}}
|
</a>{' '}
|
||||||
placeholder="请输入欢迎语"
|
获取。
|
||||||
/>
|
<br />
|
||||||
</TitleCard>
|
智能体 ID 可参考{' '}
|
||||||
|
<a
|
||||||
|
href="https://www.coze.cn/open/docs/developer_guides/coze_api_overview#c5ac4993"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
style={{ color: 'gray' }}
|
||||||
|
>
|
||||||
|
发送请求
|
||||||
|
</a>{' '}
|
||||||
|
获取。
|
||||||
|
<br />
|
||||||
|
请注意智能体发布时须勾选 API 调用能力,否则无法成功对话。
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconExclamationCircle />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: MODEL_MODE.VENDOR,
|
||||||
|
label: (
|
||||||
|
<div className={styles['radio-text']}>
|
||||||
|
<span style={{ marginRight: '4px' }}>第三方模型</span>
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
<div>
|
||||||
|
如第三方模型使用失败, 可前往{' '}
|
||||||
|
<a
|
||||||
|
href="https://www.volcengine.com/docs/6348/1399966"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
style={{ color: 'gray' }}
|
||||||
|
>
|
||||||
|
第三方模型接口验证工具
|
||||||
|
</a>{' '}
|
||||||
|
下载工具定位原因。
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconExclamationCircle />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
value={modelMode}
|
||||||
|
size="mini"
|
||||||
|
type="button"
|
||||||
|
defaultValue="Beijing"
|
||||||
|
className={styles['ai-settings-radio']}
|
||||||
|
onChange={handleUseThirdPart}
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
className={styles['ai-settings']}
|
className={styles['ai-settings']}
|
||||||
style={{
|
style={{
|
||||||
@ -272,7 +349,29 @@ function AISettings({ open, onCancel, onOk }: IAISettingsProps) {
|
|||||||
</div>
|
</div>
|
||||||
</TitleCard>
|
</TitleCard>
|
||||||
<div className={styles['ai-settings-model']}>
|
<div className={styles['ai-settings-model']}>
|
||||||
{use3Part ? (
|
{modelMode === MODEL_MODE.ORIGINAL && (
|
||||||
|
<TitleCard title="官方模型">
|
||||||
|
<CheckBoxSelector
|
||||||
|
label="模型选择"
|
||||||
|
data={Object.keys(AI_MODEL).map((type) => ({
|
||||||
|
key: AI_MODEL[type as keyof typeof AI_MODEL],
|
||||||
|
label: type.replaceAll('_', ' '),
|
||||||
|
icon: DoubaoModelSVG,
|
||||||
|
}))}
|
||||||
|
moreIcon={ModelChangeSVG}
|
||||||
|
moreText="更换模型"
|
||||||
|
placeHolder="请选择你需要的模型"
|
||||||
|
onChange={(key) => {
|
||||||
|
setData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
model: key as AI_MODEL,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
value={data.model}
|
||||||
|
/>
|
||||||
|
</TitleCard>
|
||||||
|
)}
|
||||||
|
{modelMode === MODEL_MODE.VENDOR && (
|
||||||
<>
|
<>
|
||||||
<TitleCard required title="第三方模型地址">
|
<TitleCard required title="第三方模型地址">
|
||||||
<Input.TextArea
|
<Input.TextArea
|
||||||
@ -314,34 +413,68 @@ function AISettings({ open, onCancel, onOk }: IAISettingsProps) {
|
|||||||
/>
|
/>
|
||||||
</TitleCard>
|
</TitleCard>
|
||||||
</>
|
</>
|
||||||
) : (
|
|
||||||
<TitleCard title="官方模型">
|
|
||||||
<CheckBoxSelector
|
|
||||||
label="模型选择"
|
|
||||||
data={Object.keys(AI_MODEL).map((type) => ({
|
|
||||||
key: AI_MODEL[type as keyof typeof AI_MODEL],
|
|
||||||
label: type.replaceAll('_', ' '),
|
|
||||||
icon: DoubaoModelSVG,
|
|
||||||
}))}
|
|
||||||
moreIcon={ModelChangeSVG}
|
|
||||||
moreText="更换模型"
|
|
||||||
placeHolder="请选择你需要的模型"
|
|
||||||
onChange={(key) => {
|
|
||||||
setData((prev) => ({
|
|
||||||
...prev,
|
|
||||||
model: key as AI_MODEL,
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
value={data.model}
|
|
||||||
/>
|
|
||||||
</TitleCard>
|
|
||||||
)}
|
)}
|
||||||
|
{modelMode === MODEL_MODE.COZE && (
|
||||||
<Button size="mini" type="text" onClick={handleUseThirdPart}>
|
<>
|
||||||
{use3Part ? '使用官方模型' : '使用第三方模型'} <IconSwap />
|
<TitleCard required title="请求地址">
|
||||||
</Button>
|
<Input.TextArea autoSize disabled value="https://api.coze.cn" />
|
||||||
|
</TitleCard>
|
||||||
|
<TitleCard required title="访问令牌">
|
||||||
|
<Input.TextArea
|
||||||
|
autoSize
|
||||||
|
value={data.APIKey}
|
||||||
|
onChange={(val) => {
|
||||||
|
setData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
APIKey: val,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
placeholder="请输入访问令牌"
|
||||||
|
/>
|
||||||
|
</TitleCard>
|
||||||
|
<TitleCard required title="智能体 ID">
|
||||||
|
<Input.TextArea
|
||||||
|
autoSize
|
||||||
|
value={data.BotID}
|
||||||
|
onChange={(val) => {
|
||||||
|
setData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
BotID: val,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
placeholder="请输入智能体 ID"
|
||||||
|
/>
|
||||||
|
</TitleCard>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<TitleCard title="系统 Prompt">
|
||||||
|
<Input.TextArea
|
||||||
|
autoSize
|
||||||
|
value={data.prompt}
|
||||||
|
onChange={(val) => {
|
||||||
|
setData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
prompt: val,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
placeholder="请输入你需要的 Prompt 设定"
|
||||||
|
/>
|
||||||
|
</TitleCard>
|
||||||
|
<TitleCard title="欢迎语">
|
||||||
|
<Input.TextArea
|
||||||
|
autoSize
|
||||||
|
value={data.welcome}
|
||||||
|
onChange={(val) => {
|
||||||
|
setData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
welcome: val,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
placeholder="请输入欢迎语"
|
||||||
|
/>
|
||||||
|
</TitleCard>
|
||||||
</div>
|
</div>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import AISettings from '../AISettings';
|
|||||||
import style from './index.module.less';
|
import style from './index.module.less';
|
||||||
import DouBaoAvatar from '@/assets/img/DoubaoAvatarGIF.webp';
|
import DouBaoAvatar from '@/assets/img/DoubaoAvatarGIF.webp';
|
||||||
import { RootState } from '@/store';
|
import { RootState } from '@/store';
|
||||||
import { Name, VOICE_TYPE } from '@/config';
|
import { MODEL_MODE, Name, VOICE_TYPE } from '@/config';
|
||||||
|
|
||||||
interface IAvatarCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
interface IAvatarCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
@ -24,11 +24,16 @@ const ReversedVoiceType = Object.entries(VOICE_TYPE).reduce<Record<string, strin
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const SourceName = {
|
||||||
|
[MODEL_MODE.VENDOR]: '第三方模型',
|
||||||
|
[MODEL_MODE.COZE]: 'Coze',
|
||||||
|
};
|
||||||
|
|
||||||
function AvatarCard(props: IAvatarCardProps) {
|
function AvatarCard(props: IAvatarCardProps) {
|
||||||
const room = useSelector((state: RootState) => state.room);
|
const room = useSelector((state: RootState) => state.room);
|
||||||
|
const { scene, aiConfig, modelMode } = room;
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const scene = room.scene;
|
const { LLMConfig, TTSConfig } = aiConfig.Config || {};
|
||||||
const { LLMConfig, TTSConfig } = room.aiConfig.Config || {};
|
|
||||||
const { avatar, className, ...rest } = props;
|
const { avatar, className, ...rest } = props;
|
||||||
const voice = TTSConfig.ProviderParams.audio.voice_type;
|
const voice = TTSConfig.ProviderParams.audio.voice_type;
|
||||||
|
|
||||||
@ -51,7 +56,11 @@ function AvatarCard(props: IAvatarCardProps) {
|
|||||||
<div className={style['user-info']}>
|
<div className={style['user-info']}>
|
||||||
<div className={style.title}>{Name[scene]}</div>
|
<div className={style.title}>{Name[scene]}</div>
|
||||||
<div className={style.description}>声源来自 {ReversedVoiceType[voice || '']}</div>
|
<div className={style.description}>声源来自 {ReversedVoiceType[voice || '']}</div>
|
||||||
<div className={style.description}>模型 {LLMConfig.ModelName}</div>
|
<div className={style.description}>
|
||||||
|
{modelMode === MODEL_MODE.ORIGINAL
|
||||||
|
? `模型 ${LLMConfig.ModelName}`
|
||||||
|
: `模型来源 ${SourceName[modelMode]}`}
|
||||||
|
</div>
|
||||||
<AISettings open={open} onOk={handleCloseDrawer} onCancel={handleCloseDrawer} />
|
<AISettings open={open} onOk={handleCloseDrawer} onCancel={handleCloseDrawer} />
|
||||||
<Button className={style.button} onClick={handleOpenDrawer}>
|
<Button className={style.button} onClick={handleOpenDrawer}>
|
||||||
<div className={style['button-text']}>修改 AI 设定</div>
|
<div className={style['button-text']}>修改 AI 设定</div>
|
||||||
|
|||||||
@ -24,6 +24,12 @@ export enum CustomParamsType {
|
|||||||
LLM = 'LLM',
|
LLM = 'LLM',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum MODEL_MODE {
|
||||||
|
ORIGINAL = 'original',
|
||||||
|
VENDOR = 'vendor',
|
||||||
|
COZE = 'coze',
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief AI 音色可选值
|
* @brief AI 音色可选值
|
||||||
* @default 通用女声
|
* @default 通用女声
|
||||||
@ -328,4 +334,4 @@ export const Prompt = {
|
|||||||
[SCENE.CUSTOM]: '',
|
[SCENE.CUSTOM]: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isVisionMode = (model: AI_MODEL) => model.startsWith('Vision');
|
export const isVisionMode = (model?: AI_MODEL) => model?.startsWith('Vision');
|
||||||
|
|||||||
@ -7,13 +7,12 @@ import { StreamIndex } from '@volcengine/rtc';
|
|||||||
import {
|
import {
|
||||||
TTS_CLUSTER,
|
TTS_CLUSTER,
|
||||||
ARK_V3_MODEL_ID,
|
ARK_V3_MODEL_ID,
|
||||||
ModelSourceType,
|
MODEL_MODE,
|
||||||
SCENE,
|
SCENE,
|
||||||
Prompt,
|
Prompt,
|
||||||
Welcome,
|
Welcome,
|
||||||
Model,
|
Model,
|
||||||
Voice,
|
Voice,
|
||||||
// LLM_BOT_ID,
|
|
||||||
AI_MODEL,
|
AI_MODEL,
|
||||||
AI_MODE_MAP,
|
AI_MODE_MAP,
|
||||||
AI_MODEL_MODE,
|
AI_MODEL_MODE,
|
||||||
@ -98,7 +97,11 @@ export class ConfigFactory {
|
|||||||
*/
|
*/
|
||||||
WelcomeSpeech = Welcome[SCENE.INTELLIGENT_ASSISTANT];
|
WelcomeSpeech = Welcome[SCENE.INTELLIGENT_ASSISTANT];
|
||||||
|
|
||||||
ModeSourceType = ModelSourceType.Available;
|
/**
|
||||||
|
* @note 当前使用的模型来源, 具体可参考 MODEL_MODE 定义。
|
||||||
|
* 通过 UI 修改, 无须手动配置。
|
||||||
|
*/
|
||||||
|
ModeSourceType = MODEL_MODE.ORIGINAL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @note 非必填, 第三方模型才需要使用, 用火山方舟模型时无需关注。
|
* @note 非必填, 第三方模型才需要使用, 用火山方舟模型时无需关注。
|
||||||
@ -116,6 +119,11 @@ export class ConfigFactory {
|
|||||||
*/
|
*/
|
||||||
BotName = 'RobotMan_';
|
BotName = 'RobotMan_';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @note Coze 智能体 ID,可通过 UI 配置,也可以在此直接定义。
|
||||||
|
*/
|
||||||
|
BotID = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 是否为打断模式
|
* @brief 是否为打断模式
|
||||||
*/
|
*/
|
||||||
@ -130,7 +138,6 @@ export class ConfigFactory {
|
|||||||
const params: Record<string, unknown> = {
|
const params: Record<string, unknown> = {
|
||||||
Mode: AI_MODE_MAP[this.Model || ''] || AI_MODEL_MODE.CUSTOM,
|
Mode: AI_MODE_MAP[this.Model || ''] || AI_MODEL_MODE.CUSTOM,
|
||||||
EndPointId: ARK_V3_MODEL_ID[this.Model],
|
EndPointId: ARK_V3_MODEL_ID[this.Model],
|
||||||
// BotId: LLM_BOT_ID[this.Model],
|
|
||||||
MaxTokens: 1024,
|
MaxTokens: 1024,
|
||||||
Temperature: 0.1,
|
Temperature: 0.1,
|
||||||
TopP: 0.3,
|
TopP: 0.3,
|
||||||
@ -139,7 +146,6 @@ export class ConfigFactory {
|
|||||||
ModelName: this.Model,
|
ModelName: this.Model,
|
||||||
ModelVersion: '1.0',
|
ModelVersion: '1.0',
|
||||||
WelcomeSpeech: this.WelcomeSpeech,
|
WelcomeSpeech: this.WelcomeSpeech,
|
||||||
ModeSourceType: this.ModeSourceType,
|
|
||||||
APIKey: this.APIKey,
|
APIKey: this.APIKey,
|
||||||
Url: this.Url,
|
Url: this.Url,
|
||||||
Feature: JSON.stringify({ Http: true }),
|
Feature: JSON.stringify({ Http: true }),
|
||||||
@ -154,6 +160,23 @@ export class ConfigFactory {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (this.ModeSourceType === MODEL_MODE.COZE) {
|
||||||
|
/**
|
||||||
|
* @note Coze 智能体配置的相关参数, 可参考: https://www.volcengine.com/docs/6348/1404673?s=g#llmconfig%EF%BC%88coze%E5%B9%B3%E5%8F%B0%EF%BC%89
|
||||||
|
*/
|
||||||
|
return {
|
||||||
|
Mode: 'CozeBot',
|
||||||
|
CozeBotConfig: {
|
||||||
|
Url: 'https://api.coze.cn',
|
||||||
|
BotID: this.BotID,
|
||||||
|
APIKey: this.APIKey,
|
||||||
|
UserId: this.BaseConfig.UserId,
|
||||||
|
HistoryLength: 10,
|
||||||
|
Prefill: false,
|
||||||
|
EnableConversation: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import { ConfigFactory } from './config';
|
|||||||
export * from './common';
|
export * from './common';
|
||||||
|
|
||||||
export const AIGC_PROXY_HOST = 'http://localhost:3001/proxyAIGCFetch';
|
export const AIGC_PROXY_HOST = 'http://localhost:3001/proxyAIGCFetch';
|
||||||
export const DEMO_VERSION = '1.4.0';
|
|
||||||
|
|
||||||
export const Config = ConfigFactory;
|
export const Config = ConfigFactory;
|
||||||
export default new ConfigFactory();
|
export default new ConfigFactory();
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import {
|
|||||||
NetworkQuality,
|
NetworkQuality,
|
||||||
RemoteAudioStats,
|
RemoteAudioStats,
|
||||||
} from '@volcengine/rtc';
|
} from '@volcengine/rtc';
|
||||||
import config, { SCENE } from '@/config';
|
import config, { MODEL_MODE, SCENE } from '@/config';
|
||||||
|
|
||||||
export interface IUser {
|
export interface IUser {
|
||||||
username?: string;
|
username?: string;
|
||||||
@ -71,6 +71,10 @@ export interface RoomState {
|
|||||||
* @brief AI 基础配置
|
* @brief AI 基础配置
|
||||||
*/
|
*/
|
||||||
aiConfig: ReturnType<any>;
|
aiConfig: ReturnType<any>;
|
||||||
|
/**
|
||||||
|
* @brief 当前模型的类型
|
||||||
|
*/
|
||||||
|
modelMode: MODEL_MODE;
|
||||||
/**
|
/**
|
||||||
* @brief 网络质量
|
* @brief 网络质量
|
||||||
*/
|
*/
|
||||||
@ -116,6 +120,7 @@ const initialState: RoomState = {
|
|||||||
networkQuality: NetworkQuality.UNKNOWN,
|
networkQuality: NetworkQuality.UNKNOWN,
|
||||||
|
|
||||||
aiConfig: config.aigcConfig,
|
aiConfig: config.aigcConfig,
|
||||||
|
modelMode: MODEL_MODE.ORIGINAL,
|
||||||
|
|
||||||
msgHistory: [],
|
msgHistory: [],
|
||||||
currentConversation: {},
|
currentConversation: {},
|
||||||
@ -225,6 +230,9 @@ export const roomSlice = createSlice({
|
|||||||
updateAIConfig: (state, { payload }) => {
|
updateAIConfig: (state, { payload }) => {
|
||||||
state.aiConfig = Object.assign(state.aiConfig, payload);
|
state.aiConfig = Object.assign(state.aiConfig, payload);
|
||||||
},
|
},
|
||||||
|
updateModelMode: (state, { payload }) => {
|
||||||
|
state.modelMode = payload;
|
||||||
|
},
|
||||||
clearHistoryMsg: (state) => {
|
clearHistoryMsg: (state) => {
|
||||||
state.msgHistory = [];
|
state.msgHistory = [];
|
||||||
},
|
},
|
||||||
@ -307,6 +315,7 @@ export const {
|
|||||||
updateAITalkState,
|
updateAITalkState,
|
||||||
updateAIThinkState,
|
updateAIThinkState,
|
||||||
updateAIConfig,
|
updateAIConfig,
|
||||||
|
updateModelMode,
|
||||||
setHistoryMsg,
|
setHistoryMsg,
|
||||||
clearHistoryMsg,
|
clearHistoryMsg,
|
||||||
clearCurrentMsg,
|
clearCurrentMsg,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user