Refactor assistant greeting logic to conditionally use system prompt for generated openers. Update related tests to verify new behavior and ensure correct metadata handling in API responses. Enhance UI to reflect changes in opener management based on generated opener settings.

This commit is contained in:
Xin Wang
2026-03-02 02:38:45 +08:00
parent 31b3969b96
commit 4d553de34d
5 changed files with 98 additions and 51 deletions

View File

@@ -889,25 +889,26 @@ export const AssistantsPage: React.FC = () => {
</button>
</div>
</div>
<div className="relative">
<Input
{selectedAssistant.generatedOpenerEnabled === true ? (
<div className="rounded-md border border-dashed border-white/15 bg-white/5 px-3 py-2 text-xs text-muted-foreground">
使
</div>
) : (
<div className="relative">
<Input
value={selectedAssistant.opener}
onChange={(e) => {
const next = e.target.value;
updateAssistant('opener', next);
if (selectedAssistant.generatedOpenerEnabled === true) return;
updateTemplateSuggestionState('opener', next, e.currentTarget.selectionStart, e.currentTarget);
}}
onKeyUp={(e) => {
if (selectedAssistant.generatedOpenerEnabled === true) return;
updateTemplateSuggestionState('opener', e.currentTarget.value, e.currentTarget.selectionStart, e.currentTarget);
}}
onClick={(e) => {
if (selectedAssistant.generatedOpenerEnabled === true) return;
updateTemplateSuggestionState('opener', e.currentTarget.value, e.currentTarget.selectionStart, e.currentTarget);
}}
onFocus={(e) => {
if (selectedAssistant.generatedOpenerEnabled === true) return;
updateTemplateSuggestionState('opener', e.currentTarget.value, e.currentTarget.selectionStart, e.currentTarget);
}}
onBlur={() => {
@@ -915,40 +916,39 @@ export const AssistantsPage: React.FC = () => {
setTemplateSuggestion((prev) => (prev?.field === 'opener' ? null : prev));
}, 120);
}}
placeholder={selectedAssistant.generatedOpenerEnabled === true ? '将基于提示词自动生成开场白' : '例如您好我是您的专属AI助手...'}
disabled={selectedAssistant.generatedOpenerEnabled === true}
className="bg-white/5 border-white/10 focus:border-primary/50 disabled:opacity-50 disabled:cursor-not-allowed"
/>
{templateSuggestion?.field === 'opener' &&
filteredSystemTemplateVariables.length > 0 &&
selectedAssistant.generatedOpenerEnabled !== true &&
typeof document !== 'undefined' &&
createPortal(
<div
className="fixed z-[100] w-[320px] max-w-[calc(100vw-1rem)] rounded-md border border-white/15 bg-black/95 shadow-xl backdrop-blur-md max-h-48 overflow-auto"
style={{
left: templateSuggestion.anchorLeft,
top: templateSuggestion.anchorTop,
}}
>
{filteredSystemTemplateVariables.map((item) => (
<button
key={item.key}
type="button"
className="w-full text-left px-3 py-2 hover:bg-white/10 transition-colors"
onMouseDown={(e) => {
e.preventDefault();
applySystemTemplateVariable('opener', item.key);
}}
>
<div className="text-xs text-cyan-100">{`{{${item.key}}}`}</div>
<div className="text-[10px] text-muted-foreground mt-0.5">{item.description}</div>
</button>
))}
</div>,
document.body
)}
</div>
placeholder="例如您好我是您的专属AI助手..."
className="bg-white/5 border-white/10 focus:border-primary/50"
/>
{templateSuggestion?.field === 'opener' &&
filteredSystemTemplateVariables.length > 0 &&
typeof document !== 'undefined' &&
createPortal(
<div
className="fixed z-[100] w-[320px] max-w-[calc(100vw-1rem)] rounded-md border border-white/15 bg-black/95 shadow-xl backdrop-blur-md max-h-48 overflow-auto"
style={{
left: templateSuggestion.anchorLeft,
top: templateSuggestion.anchorTop,
}}
>
{filteredSystemTemplateVariables.map((item) => (
<button
key={item.key}
type="button"
className="w-full text-left px-3 py-2 hover:bg-white/10 transition-colors"
onMouseDown={(e) => {
e.preventDefault();
applySystemTemplateVariable('opener', item.key);
}}
>
<div className="text-xs text-cyan-100">{`{{${item.key}}}`}</div>
<div className="text-[10px] text-muted-foreground mt-0.5">{item.description}</div>
</button>
))}
</div>,
document.body
)}
</div>
)}
<p className="text-xs text-muted-foreground">
{selectedAssistant.generatedOpenerEnabled === true
? '通话接通后将根据提示词自动生成开场白。'
@@ -2139,10 +2139,13 @@ export const DebugDrawer: React.FC<{
|| textSessionStarted;
const requiredTemplateVariableKeys = useMemo(() => {
const keys = new Set<string>();
const includeOpenerTemplate = assistant.generatedOpenerEnabled !== true;
extractDynamicTemplateKeys(String(assistant.prompt || '')).forEach((key) => keys.add(key));
extractDynamicTemplateKeys(String(assistant.opener || '')).forEach((key) => keys.add(key));
if (includeOpenerTemplate) {
extractDynamicTemplateKeys(String(assistant.opener || '')).forEach((key) => keys.add(key));
}
return Array.from(keys).sort();
}, [assistant.opener, assistant.prompt]);
}, [assistant.generatedOpenerEnabled, assistant.opener, assistant.prompt]);
const missingRequiredDynamicVariableKeys = useMemo(() => {
const valuesByKey = new Map<string, string>();
for (const row of dynamicVariables) {
@@ -3142,6 +3145,7 @@ export const DebugDrawer: React.FC<{
const buildLocalResolvedRuntime = () => {
const warnings: string[] = [];
const ttsEnabled = Boolean(textTtsEnabled);
const generatedOpenerEnabled = assistant.generatedOpenerEnabled === true;
const knowledgeBaseId = String(assistant.knowledgeBaseId || '').trim();
const knowledge = knowledgeBaseId
? { enabled: true, kbId: knowledgeBaseId, nResults: 5 }
@@ -3156,8 +3160,8 @@ export const DebugDrawer: React.FC<{
},
systemPrompt: assistant.prompt || '',
firstTurnMode: assistant.firstTurnMode || 'bot_first',
greeting: assistant.opener || '',
generatedOpenerEnabled: assistant.generatedOpenerEnabled === true,
greeting: generatedOpenerEnabled ? '' : (assistant.opener || ''),
generatedOpenerEnabled,
bargeIn: {
enabled: assistant.botCannotBeInterrupted !== true,
minDurationMs: Math.max(0, Number(assistant.interruptionSensitivity ?? 180)),
@@ -3965,7 +3969,7 @@ export const DebugDrawer: React.FC<{
className="h-6 px-2 text-[10px]"
onClick={importDynamicVariablesFromPlaceholders}
disabled={isDynamicVariablesLocked || requiredTemplateVariableKeys.length === 0 || dynamicVariables.length >= DYNAMIC_VARIABLE_MAX_ITEMS}
title="Import keys from {{placeholder}} in prompt/opener"
title={`Import keys from {{placeholder}} in prompt${assistant.generatedOpenerEnabled === true ? '' : '/opener'}`}
>
Import
</Button>
@@ -3982,7 +3986,7 @@ export const DebugDrawer: React.FC<{
</div>
</div>
<p className="text-[11px] text-muted-foreground">
Use placeholders like {'{{customer_name}}'} in prompt/opener.
Use placeholders like {'{{customer_name}}'} in prompt{assistant.generatedOpenerEnabled === true ? '' : '/opener'}.
</p>
<p className="text-[11px] text-muted-foreground">
Built-in system vars: {'{{system__time}}'}, {'{{system_utc}}'}, {'{{system_timezone}}'}.