Add bot not interrupt and generated opener
This commit is contained in:
@@ -118,6 +118,7 @@ export const AssistantsPage: React.FC = () => {
|
||||
const newAssistantPayload: Partial<Assistant> = {
|
||||
name: 'New Assistant',
|
||||
opener: '',
|
||||
generatedOpenerEnabled: false,
|
||||
prompt: '',
|
||||
knowledgeBaseId: '',
|
||||
language: 'zh',
|
||||
@@ -126,6 +127,7 @@ export const AssistantsPage: React.FC = () => {
|
||||
speed: 1,
|
||||
hotwords: [],
|
||||
tools: [],
|
||||
botCannotBeInterrupted: false,
|
||||
interruptionSensitivity: 500,
|
||||
configMode: 'platform',
|
||||
};
|
||||
@@ -244,6 +246,7 @@ export const AssistantsPage: React.FC = () => {
|
||||
|
||||
const isExternalConfig = selectedAssistant?.configMode === 'dify' || selectedAssistant?.configMode === 'fastgpt';
|
||||
const isNoneConfig = selectedAssistant?.configMode === 'none' || !selectedAssistant?.configMode;
|
||||
const canAdjustInterruptionSensitivity = selectedAssistant?.botCannotBeInterrupted !== true;
|
||||
|
||||
return (
|
||||
<div className="flex h-full min-h-0 gap-6 animate-in fade-in">
|
||||
@@ -524,11 +527,30 @@ export const AssistantsPage: React.FC = () => {
|
||||
value={selectedAssistant.opener}
|
||||
onChange={(e) => updateAssistant('opener', e.target.value)}
|
||||
placeholder="例如:您好,我是您的专属AI助手..."
|
||||
className="bg-white/5 border-white/10 focus:border-primary/50"
|
||||
disabled={selectedAssistant.generatedOpenerEnabled === true}
|
||||
className="bg-white/5 border-white/10 focus:border-primary/50 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">接通通话后的第一句话。</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-white flex items-center">
|
||||
<Sparkles className="w-4 h-4 mr-2 text-primary" /> Generated Opener
|
||||
</label>
|
||||
<label className="flex h-12 items-center justify-between rounded-xl border border-white/10 bg-white/5 px-4 text-sm">
|
||||
<span className="text-foreground">自动生成开场白</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedAssistant.generatedOpenerEnabled === true}
|
||||
onChange={(e) => updateAssistant('generatedOpenerEnabled', e.target.checked)}
|
||||
className="accent-primary"
|
||||
/>
|
||||
</label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
开启后,系统将自动生成开场白,手动开场白输入框会暂时禁用。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-white flex items-center">
|
||||
<BotIcon className="w-4 h-4 mr-2 text-primary"/> 提示词 (Prompt)
|
||||
@@ -679,8 +701,23 @@ export const AssistantsPage: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 pt-2">
|
||||
<div className="flex justify-between items-center mb-1">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-white flex items-center">
|
||||
<ArrowLeftRight className="w-4 h-4 mr-2 text-primary" /> Bot cannot be interrupted
|
||||
</label>
|
||||
<label className="flex h-12 items-center justify-between rounded-xl border border-white/10 bg-white/5 px-4 text-sm">
|
||||
<span className="text-foreground">机器人不可打断</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedAssistant.botCannotBeInterrupted === true}
|
||||
onChange={(e) => updateAssistant('botCannotBeInterrupted', e.target.checked)}
|
||||
className="accent-primary"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center mb-1">
|
||||
<label className={`text-sm font-medium flex items-center ${canAdjustInterruptionSensitivity ? 'text-white' : 'text-muted-foreground'}`}>
|
||||
<Timer className="w-4 h-4 mr-2 text-primary"/> 打断灵敏度 (Interruption Sensitivity)
|
||||
</label>
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -689,7 +726,8 @@ export const AssistantsPage: React.FC = () => {
|
||||
type="number"
|
||||
value={selectedAssistant.interruptionSensitivity || 500}
|
||||
onChange={(e) => updateAssistant('interruptionSensitivity', parseInt(e.target.value) || 0)}
|
||||
className="w-20 h-8 text-right pr-7 text-xs font-mono bg-black/40 border-white/5"
|
||||
disabled={!canAdjustInterruptionSensitivity}
|
||||
className="w-20 h-8 text-right pr-7 text-xs font-mono bg-black/40 border-white/5 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
/>
|
||||
<span className="absolute right-2 top-1/2 -translate-y-1/2 text-[10px] text-muted-foreground font-mono">ms</span>
|
||||
</div>
|
||||
@@ -703,16 +741,19 @@ export const AssistantsPage: React.FC = () => {
|
||||
step="50"
|
||||
value={selectedAssistant.interruptionSensitivity || 500}
|
||||
onChange={(e) => updateAssistant('interruptionSensitivity', parseInt(e.target.value))}
|
||||
className="flex-1 h-1.5 bg-secondary rounded-lg appearance-none cursor-pointer accent-primary"
|
||||
disabled={!canAdjustInterruptionSensitivity}
|
||||
className="flex-1 h-1.5 bg-secondary rounded-lg appearance-none cursor-pointer accent-primary disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-between text-[10px] text-muted-foreground font-mono uppercase tracking-widest px-0.5 opacity-50">
|
||||
<div className={`flex justify-between text-[10px] font-mono uppercase tracking-widest px-0.5 ${canAdjustInterruptionSensitivity ? 'text-muted-foreground opacity-50' : 'text-muted-foreground/60 opacity-35'}`}>
|
||||
<span>0ms (Extreme)</span>
|
||||
<span>1000ms</span>
|
||||
<span>2000ms (Lazy)</span>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground pt-1 italic opacity-60">
|
||||
* 定义用户说话多长时间后 AI 应当停止当前的发言并响应。数值越小响应越快,但也更容易被噪音误导打断。
|
||||
<p className={`text-xs pt-1 italic ${canAdjustInterruptionSensitivity ? 'text-muted-foreground opacity-60' : 'text-muted-foreground/70 opacity-50'}`}>
|
||||
{canAdjustInterruptionSensitivity
|
||||
? '* 定义用户说话多长时间后 AI 应当停止当前的发言并响应。数值越小响应越快,但也更容易被噪音误导打断。'
|
||||
: '* 当前已开启“机器人不可打断”,VAD 打断灵敏度已禁用。'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1760,6 +1801,11 @@ export const DebugDrawer: React.FC<{
|
||||
},
|
||||
systemPrompt: assistant.prompt || '',
|
||||
greeting: assistant.opener || '',
|
||||
generatedOpenerEnabled: assistant.generatedOpenerEnabled === true,
|
||||
bargeIn: {
|
||||
enabled: assistant.botCannotBeInterrupted !== true,
|
||||
minDurationMs: assistant.interruptionSensitivity || 500,
|
||||
},
|
||||
knowledgeBaseId,
|
||||
knowledge,
|
||||
tools: selectedToolSchemas,
|
||||
|
||||
@@ -30,6 +30,7 @@ const mapAssistant = (raw: AnyRecord): Assistant => ({
|
||||
name: readField(raw, ['name'], ''),
|
||||
callCount: Number(readField(raw, ['callCount', 'call_count'], 0)),
|
||||
opener: readField(raw, ['opener'], ''),
|
||||
generatedOpenerEnabled: Boolean(readField(raw, ['generatedOpenerEnabled', 'generated_opener_enabled'], false)),
|
||||
prompt: readField(raw, ['prompt'], ''),
|
||||
knowledgeBaseId: readField(raw, ['knowledgeBaseId', 'knowledge_base_id'], ''),
|
||||
language: readField(raw, ['language'], 'zh') as 'zh' | 'en',
|
||||
@@ -38,6 +39,7 @@ const mapAssistant = (raw: AnyRecord): Assistant => ({
|
||||
speed: Number(readField(raw, ['speed'], 1)),
|
||||
hotwords: readField(raw, ['hotwords'], []),
|
||||
tools: readField(raw, ['tools'], []),
|
||||
botCannotBeInterrupted: Boolean(readField(raw, ['botCannotBeInterrupted', 'bot_cannot_be_interrupted'], false)),
|
||||
interruptionSensitivity: Number(readField(raw, ['interruptionSensitivity', 'interruption_sensitivity'], 500)),
|
||||
configMode: readField(raw, ['configMode', 'config_mode'], 'platform') as 'platform' | 'dify' | 'fastgpt' | 'none',
|
||||
apiUrl: readField(raw, ['apiUrl', 'api_url'], ''),
|
||||
@@ -212,6 +214,7 @@ export const createAssistant = async (data: Partial<Assistant>): Promise<Assista
|
||||
const payload = {
|
||||
name: data.name || 'New Assistant',
|
||||
opener: data.opener || '',
|
||||
generatedOpenerEnabled: data.generatedOpenerEnabled ?? false,
|
||||
prompt: data.prompt || '',
|
||||
knowledgeBaseId: data.knowledgeBaseId || '',
|
||||
language: data.language || 'zh',
|
||||
@@ -220,6 +223,7 @@ export const createAssistant = async (data: Partial<Assistant>): Promise<Assista
|
||||
speed: data.speed ?? 1,
|
||||
hotwords: data.hotwords || [],
|
||||
tools: data.tools || [],
|
||||
botCannotBeInterrupted: data.botCannotBeInterrupted ?? false,
|
||||
interruptionSensitivity: data.interruptionSensitivity ?? 500,
|
||||
configMode: data.configMode || 'platform',
|
||||
apiUrl: data.apiUrl || '',
|
||||
@@ -237,6 +241,7 @@ export const updateAssistant = async (id: string, data: Partial<Assistant>): Pro
|
||||
const payload = {
|
||||
name: data.name,
|
||||
opener: data.opener,
|
||||
generatedOpenerEnabled: data.generatedOpenerEnabled,
|
||||
prompt: data.prompt,
|
||||
knowledgeBaseId: data.knowledgeBaseId,
|
||||
language: data.language,
|
||||
@@ -245,6 +250,7 @@ export const updateAssistant = async (id: string, data: Partial<Assistant>): Pro
|
||||
speed: data.speed,
|
||||
hotwords: data.hotwords,
|
||||
tools: data.tools,
|
||||
botCannotBeInterrupted: data.botCannotBeInterrupted,
|
||||
interruptionSensitivity: data.interruptionSensitivity,
|
||||
configMode: data.configMode,
|
||||
apiUrl: data.apiUrl,
|
||||
|
||||
@@ -4,6 +4,7 @@ export interface Assistant {
|
||||
name: string;
|
||||
callCount: number;
|
||||
opener: string;
|
||||
generatedOpenerEnabled?: boolean;
|
||||
prompt: string;
|
||||
knowledgeBaseId: string;
|
||||
language: 'zh' | 'en';
|
||||
@@ -12,6 +13,7 @@ export interface Assistant {
|
||||
speed: number;
|
||||
hotwords: string[];
|
||||
tools?: string[]; // IDs of enabled tools
|
||||
botCannotBeInterrupted?: boolean;
|
||||
interruptionSensitivity?: number; // In ms
|
||||
configMode?: 'platform' | 'dify' | 'fastgpt' | 'none';
|
||||
apiUrl?: string;
|
||||
|
||||
Reference in New Issue
Block a user