Add first turn option
This commit is contained in:
@@ -112,6 +112,7 @@ class Assistant(Base):
|
|||||||
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), index=True)
|
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), index=True)
|
||||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||||
call_count: Mapped[int] = mapped_column(Integer, default=0)
|
call_count: Mapped[int] = mapped_column(Integer, default=0)
|
||||||
|
first_turn_mode: Mapped[str] = mapped_column(String(32), default="bot_first")
|
||||||
opener: Mapped[str] = mapped_column(Text, default="")
|
opener: Mapped[str] = mapped_column(Text, default="")
|
||||||
generated_opener_enabled: Mapped[bool] = mapped_column(default=False)
|
generated_opener_enabled: Mapped[bool] = mapped_column(default=False)
|
||||||
prompt: Mapped[str] = mapped_column(Text, default="")
|
prompt: Mapped[str] = mapped_column(Text, default="")
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ def _is_siliconflow_vendor(vendor: Optional[str]) -> bool:
|
|||||||
def _resolve_runtime_metadata(db: Session, assistant: Assistant) -> dict:
|
def _resolve_runtime_metadata(db: Session, assistant: Assistant) -> dict:
|
||||||
metadata = {
|
metadata = {
|
||||||
"systemPrompt": assistant.prompt or "",
|
"systemPrompt": assistant.prompt or "",
|
||||||
|
"firstTurnMode": assistant.first_turn_mode or "bot_first",
|
||||||
"greeting": assistant.opener or "",
|
"greeting": assistant.opener or "",
|
||||||
"generatedOpenerEnabled": bool(assistant.generated_opener_enabled),
|
"generatedOpenerEnabled": bool(assistant.generated_opener_enabled),
|
||||||
"output": {"mode": "audio" if assistant.voice_output_enabled else "text"},
|
"output": {"mode": "audio" if assistant.voice_output_enabled else "text"},
|
||||||
@@ -104,6 +105,7 @@ def assistant_to_dict(assistant: Assistant) -> dict:
|
|||||||
"id": assistant.id,
|
"id": assistant.id,
|
||||||
"name": assistant.name,
|
"name": assistant.name,
|
||||||
"callCount": assistant.call_count,
|
"callCount": assistant.call_count,
|
||||||
|
"firstTurnMode": assistant.first_turn_mode or "bot_first",
|
||||||
"opener": assistant.opener or "",
|
"opener": assistant.opener or "",
|
||||||
"generatedOpenerEnabled": bool(assistant.generated_opener_enabled),
|
"generatedOpenerEnabled": bool(assistant.generated_opener_enabled),
|
||||||
"prompt": assistant.prompt or "",
|
"prompt": assistant.prompt or "",
|
||||||
@@ -131,6 +133,7 @@ def assistant_to_dict(assistant: Assistant) -> dict:
|
|||||||
def _apply_assistant_update(assistant: Assistant, update_data: dict) -> None:
|
def _apply_assistant_update(assistant: Assistant, update_data: dict) -> None:
|
||||||
field_map = {
|
field_map = {
|
||||||
"knowledgeBaseId": "knowledge_base_id",
|
"knowledgeBaseId": "knowledge_base_id",
|
||||||
|
"firstTurnMode": "first_turn_mode",
|
||||||
"interruptionSensitivity": "interruption_sensitivity",
|
"interruptionSensitivity": "interruption_sensitivity",
|
||||||
"botCannotBeInterrupted": "bot_cannot_be_interrupted",
|
"botCannotBeInterrupted": "bot_cannot_be_interrupted",
|
||||||
"configMode": "config_mode",
|
"configMode": "config_mode",
|
||||||
@@ -192,6 +195,7 @@ def create_assistant(data: AssistantCreate, db: Session = Depends(get_db)):
|
|||||||
id=str(uuid.uuid4())[:8],
|
id=str(uuid.uuid4())[:8],
|
||||||
user_id=1, # 默认用户,后续添加认证
|
user_id=1, # 默认用户,后续添加认证
|
||||||
name=data.name,
|
name=data.name,
|
||||||
|
first_turn_mode=data.firstTurnMode,
|
||||||
opener=data.opener,
|
opener=data.opener,
|
||||||
generated_opener_enabled=data.generatedOpenerEnabled,
|
generated_opener_enabled=data.generatedOpenerEnabled,
|
||||||
prompt=data.prompt,
|
prompt=data.prompt,
|
||||||
|
|||||||
@@ -272,6 +272,7 @@ class ToolResourceOut(ToolResourceBase):
|
|||||||
# ============ Assistant ============
|
# ============ Assistant ============
|
||||||
class AssistantBase(BaseModel):
|
class AssistantBase(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
|
firstTurnMode: str = "bot_first"
|
||||||
opener: str = ""
|
opener: str = ""
|
||||||
generatedOpenerEnabled: bool = False
|
generatedOpenerEnabled: bool = False
|
||||||
prompt: str = ""
|
prompt: str = ""
|
||||||
@@ -300,6 +301,7 @@ class AssistantCreate(AssistantBase):
|
|||||||
|
|
||||||
class AssistantUpdate(BaseModel):
|
class AssistantUpdate(BaseModel):
|
||||||
name: Optional[str] = None
|
name: Optional[str] = None
|
||||||
|
firstTurnMode: Optional[str] = None
|
||||||
opener: Optional[str] = None
|
opener: Optional[str] = None
|
||||||
generatedOpenerEnabled: Optional[bool] = None
|
generatedOpenerEnabled: Optional[bool] = None
|
||||||
prompt: Optional[str] = None
|
prompt: Optional[str] = None
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ def migrate_db_schema():
|
|||||||
alter_statements.append(
|
alter_statements.append(
|
||||||
"ALTER TABLE assistants ADD COLUMN generated_opener_enabled BOOLEAN DEFAULT 0"
|
"ALTER TABLE assistants ADD COLUMN generated_opener_enabled BOOLEAN DEFAULT 0"
|
||||||
)
|
)
|
||||||
|
if "first_turn_mode" not in columns:
|
||||||
|
alter_statements.append(
|
||||||
|
"ALTER TABLE assistants ADD COLUMN first_turn_mode VARCHAR(32) DEFAULT 'bot_first'"
|
||||||
|
)
|
||||||
if "bot_cannot_be_interrupted" not in columns:
|
if "bot_cannot_be_interrupted" not in columns:
|
||||||
alter_statements.append(
|
alter_statements.append(
|
||||||
"ALTER TABLE assistants ADD COLUMN bot_cannot_be_interrupted BOOLEAN DEFAULT 0"
|
"ALTER TABLE assistants ADD COLUMN bot_cannot_be_interrupted BOOLEAN DEFAULT 0"
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class TestAssistantAPI:
|
|||||||
assert data["prompt"] == sample_assistant_data["prompt"]
|
assert data["prompt"] == sample_assistant_data["prompt"]
|
||||||
assert data["language"] == sample_assistant_data["language"]
|
assert data["language"] == sample_assistant_data["language"]
|
||||||
assert data["voiceOutputEnabled"] is True
|
assert data["voiceOutputEnabled"] is True
|
||||||
|
assert data["firstTurnMode"] == "bot_first"
|
||||||
assert data["generatedOpenerEnabled"] is False
|
assert data["generatedOpenerEnabled"] is False
|
||||||
assert data["botCannotBeInterrupted"] is False
|
assert data["botCannotBeInterrupted"] is False
|
||||||
assert "id" in data
|
assert "id" in data
|
||||||
@@ -230,6 +231,7 @@ class TestAssistantAPI:
|
|||||||
|
|
||||||
def test_assistant_interrupt_and_generated_opener_flags(self, client, sample_assistant_data):
|
def test_assistant_interrupt_and_generated_opener_flags(self, client, sample_assistant_data):
|
||||||
sample_assistant_data.update({
|
sample_assistant_data.update({
|
||||||
|
"firstTurnMode": "user_first",
|
||||||
"generatedOpenerEnabled": True,
|
"generatedOpenerEnabled": True,
|
||||||
"botCannotBeInterrupted": True,
|
"botCannotBeInterrupted": True,
|
||||||
"interruptionSensitivity": 900,
|
"interruptionSensitivity": 900,
|
||||||
@@ -241,6 +243,7 @@ class TestAssistantAPI:
|
|||||||
get_resp = client.get(f"/api/assistants/{assistant_id}")
|
get_resp = client.get(f"/api/assistants/{assistant_id}")
|
||||||
assert get_resp.status_code == 200
|
assert get_resp.status_code == 200
|
||||||
payload = get_resp.json()
|
payload = get_resp.json()
|
||||||
|
assert payload["firstTurnMode"] == "user_first"
|
||||||
assert payload["generatedOpenerEnabled"] is True
|
assert payload["generatedOpenerEnabled"] is True
|
||||||
assert payload["botCannotBeInterrupted"] is True
|
assert payload["botCannotBeInterrupted"] is True
|
||||||
assert payload["interruptionSensitivity"] == 900
|
assert payload["interruptionSensitivity"] == 900
|
||||||
@@ -248,6 +251,7 @@ class TestAssistantAPI:
|
|||||||
runtime_resp = client.get(f"/api/assistants/{assistant_id}/runtime-config")
|
runtime_resp = client.get(f"/api/assistants/{assistant_id}/runtime-config")
|
||||||
assert runtime_resp.status_code == 200
|
assert runtime_resp.status_code == 200
|
||||||
metadata = runtime_resp.json()["sessionStartMetadata"]
|
metadata = runtime_resp.json()["sessionStartMetadata"]
|
||||||
|
assert metadata["firstTurnMode"] == "user_first"
|
||||||
assert metadata["generatedOpenerEnabled"] is True
|
assert metadata["generatedOpenerEnabled"] is True
|
||||||
assert metadata["bargeIn"]["enabled"] is False
|
assert metadata["bargeIn"]["enabled"] is False
|
||||||
assert metadata["bargeIn"]["minDurationMs"] == 900
|
assert metadata["bargeIn"]["minDurationMs"] == 900
|
||||||
|
|||||||
@@ -267,6 +267,7 @@ class DuplexPipeline:
|
|||||||
self._runtime_tts: Dict[str, Any] = {}
|
self._runtime_tts: Dict[str, Any] = {}
|
||||||
self._runtime_output: Dict[str, Any] = {}
|
self._runtime_output: Dict[str, Any] = {}
|
||||||
self._runtime_system_prompt: Optional[str] = None
|
self._runtime_system_prompt: Optional[str] = None
|
||||||
|
self._runtime_first_turn_mode: str = "bot_first"
|
||||||
self._runtime_greeting: Optional[str] = None
|
self._runtime_greeting: Optional[str] = None
|
||||||
self._runtime_generated_opener_enabled: Optional[bool] = None
|
self._runtime_generated_opener_enabled: Optional[bool] = None
|
||||||
self._runtime_barge_in_enabled: Optional[bool] = None
|
self._runtime_barge_in_enabled: Optional[bool] = None
|
||||||
@@ -303,6 +304,9 @@ class DuplexPipeline:
|
|||||||
self._runtime_system_prompt = str(metadata.get("systemPrompt") or "")
|
self._runtime_system_prompt = str(metadata.get("systemPrompt") or "")
|
||||||
if self._runtime_system_prompt:
|
if self._runtime_system_prompt:
|
||||||
self.conversation.system_prompt = self._runtime_system_prompt
|
self.conversation.system_prompt = self._runtime_system_prompt
|
||||||
|
if "firstTurnMode" in metadata:
|
||||||
|
raw_mode = str(metadata.get("firstTurnMode") or "").strip().lower()
|
||||||
|
self._runtime_first_turn_mode = "user_first" if raw_mode == "user_first" else "bot_first"
|
||||||
if "greeting" in metadata:
|
if "greeting" in metadata:
|
||||||
greeting_payload = metadata.get("greeting")
|
greeting_payload = metadata.get("greeting")
|
||||||
if isinstance(greeting_payload, dict):
|
if isinstance(greeting_payload, dict):
|
||||||
@@ -393,6 +397,9 @@ class DuplexPipeline:
|
|||||||
def _generated_opener_enabled(self) -> bool:
|
def _generated_opener_enabled(self) -> bool:
|
||||||
return self._runtime_generated_opener_enabled is True
|
return self._runtime_generated_opener_enabled is True
|
||||||
|
|
||||||
|
def _bot_starts_first(self) -> bool:
|
||||||
|
return self._runtime_first_turn_mode != "user_first"
|
||||||
|
|
||||||
def _barge_in_enabled(self) -> bool:
|
def _barge_in_enabled(self) -> bool:
|
||||||
if self._runtime_barge_in_enabled is not None:
|
if self._runtime_barge_in_enabled is not None:
|
||||||
return self._runtime_barge_in_enabled
|
return self._runtime_barge_in_enabled
|
||||||
@@ -540,6 +547,7 @@ class DuplexPipeline:
|
|||||||
|
|
||||||
# Resolve greeting once per session start.
|
# Resolve greeting once per session start.
|
||||||
# Always emit text opener event so text-only sessions can display it.
|
# Always emit text opener event so text-only sessions can display it.
|
||||||
|
if self._bot_starts_first():
|
||||||
greeting_to_speak = self.conversation.greeting
|
greeting_to_speak = self.conversation.greeting
|
||||||
if self._generated_opener_enabled():
|
if self._generated_opener_enabled():
|
||||||
generated_greeting = await self._generate_runtime_greeting()
|
generated_greeting = await self._generate_runtime_greeting()
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ export const AssistantsPage: React.FC = () => {
|
|||||||
const handleCreate = async () => {
|
const handleCreate = async () => {
|
||||||
const newAssistantPayload: Partial<Assistant> = {
|
const newAssistantPayload: Partial<Assistant> = {
|
||||||
name: 'New Assistant',
|
name: 'New Assistant',
|
||||||
|
firstTurnMode: 'bot_first',
|
||||||
opener: '',
|
opener: '',
|
||||||
generatedOpenerEnabled: false,
|
generatedOpenerEnabled: false,
|
||||||
prompt: '',
|
prompt: '',
|
||||||
@@ -247,6 +248,7 @@ export const AssistantsPage: React.FC = () => {
|
|||||||
const isExternalConfig = selectedAssistant?.configMode === 'dify' || selectedAssistant?.configMode === 'fastgpt';
|
const isExternalConfig = selectedAssistant?.configMode === 'dify' || selectedAssistant?.configMode === 'fastgpt';
|
||||||
const isNoneConfig = selectedAssistant?.configMode === 'none' || !selectedAssistant?.configMode;
|
const isNoneConfig = selectedAssistant?.configMode === 'none' || !selectedAssistant?.configMode;
|
||||||
const canAdjustInterruptionSensitivity = selectedAssistant?.botCannotBeInterrupted !== true;
|
const canAdjustInterruptionSensitivity = selectedAssistant?.botCannotBeInterrupted !== true;
|
||||||
|
const isBotFirstTurn = selectedAssistant?.firstTurnMode !== 'user_first';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full min-h-0 gap-6 animate-in fade-in">
|
<div className="flex h-full min-h-0 gap-6 animate-in fade-in">
|
||||||
@@ -522,28 +524,63 @@ export const AssistantsPage: React.FC = () => {
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between gap-3">
|
<div className="flex items-center justify-between gap-3">
|
||||||
<label className="text-sm font-medium text-white flex items-center">
|
<label className="text-sm font-medium text-white flex items-center">
|
||||||
|
<PhoneCall className="w-4 h-4 mr-2 text-primary"/> 首轮发言方
|
||||||
|
</label>
|
||||||
|
<div className="inline-flex rounded-lg border border-white/10 bg-white/5 p-1">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => updateAssistant('firstTurnMode', 'bot_first')}
|
||||||
|
className={`px-3 py-1 text-xs rounded-md transition-colors ${
|
||||||
|
isBotFirstTurn
|
||||||
|
? 'bg-primary text-primary-foreground shadow-sm'
|
||||||
|
: 'text-muted-foreground hover:text-foreground'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
机器人先说
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => updateAssistant('firstTurnMode', 'user_first')}
|
||||||
|
className={`px-3 py-1 text-xs rounded-md transition-colors ${
|
||||||
|
isBotFirstTurn
|
||||||
|
? 'text-muted-foreground hover:text-foreground'
|
||||||
|
: 'bg-primary text-primary-foreground shadow-sm'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
用户先说
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-muted-foreground">决定通话接通后由谁先开始第一句对话。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={`space-y-2 transition-opacity ${isBotFirstTurn ? 'opacity-100' : 'opacity-60'}`}>
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<label className={`text-sm font-medium flex items-center ${isBotFirstTurn ? 'text-white' : 'text-muted-foreground'}`}>
|
||||||
<MessageSquare className="w-4 h-4 mr-2 text-primary"/> 开场白
|
<MessageSquare className="w-4 h-4 mr-2 text-primary"/> 开场白
|
||||||
</label>
|
</label>
|
||||||
<div className="inline-flex rounded-lg border border-white/10 bg-white/5 p-1">
|
<div className="inline-flex rounded-lg border border-white/10 bg-white/5 p-1">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
disabled={!isBotFirstTurn}
|
||||||
onClick={() => updateAssistant('generatedOpenerEnabled', false)}
|
onClick={() => updateAssistant('generatedOpenerEnabled', false)}
|
||||||
className={`px-3 py-1 text-xs rounded-md transition-colors ${
|
className={`px-3 py-1 text-xs rounded-md transition-colors ${
|
||||||
selectedAssistant.generatedOpenerEnabled === true
|
selectedAssistant.generatedOpenerEnabled === true
|
||||||
? 'text-muted-foreground hover:text-foreground'
|
? 'text-muted-foreground hover:text-foreground'
|
||||||
: 'bg-primary text-primary-foreground shadow-sm'
|
: 'bg-primary text-primary-foreground shadow-sm'
|
||||||
}`}
|
} disabled:opacity-50 disabled:cursor-not-allowed`}
|
||||||
>
|
>
|
||||||
手动输入
|
手动输入
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
disabled={!isBotFirstTurn}
|
||||||
onClick={() => updateAssistant('generatedOpenerEnabled', true)}
|
onClick={() => updateAssistant('generatedOpenerEnabled', true)}
|
||||||
className={`px-3 py-1 text-xs rounded-md transition-colors ${
|
className={`px-3 py-1 text-xs rounded-md transition-colors ${
|
||||||
selectedAssistant.generatedOpenerEnabled === true
|
selectedAssistant.generatedOpenerEnabled === true
|
||||||
? 'bg-primary text-primary-foreground shadow-sm'
|
? 'bg-primary text-primary-foreground shadow-sm'
|
||||||
: 'text-muted-foreground hover:text-foreground'
|
: 'text-muted-foreground hover:text-foreground'
|
||||||
}`}
|
} disabled:opacity-50 disabled:cursor-not-allowed`}
|
||||||
>
|
>
|
||||||
自动生成
|
自动生成
|
||||||
</button>
|
</button>
|
||||||
@@ -552,12 +589,20 @@ export const AssistantsPage: React.FC = () => {
|
|||||||
<Input
|
<Input
|
||||||
value={selectedAssistant.opener}
|
value={selectedAssistant.opener}
|
||||||
onChange={(e) => updateAssistant('opener', e.target.value)}
|
onChange={(e) => updateAssistant('opener', e.target.value)}
|
||||||
placeholder={selectedAssistant.generatedOpenerEnabled === true ? '将基于提示词自动生成开场白' : '例如:您好,我是您的专属AI助手...'}
|
placeholder={
|
||||||
disabled={selectedAssistant.generatedOpenerEnabled === true}
|
!isBotFirstTurn
|
||||||
|
? '当前为用户先说,开场白不会在首轮触发'
|
||||||
|
: selectedAssistant.generatedOpenerEnabled === true
|
||||||
|
? '将基于提示词自动生成开场白'
|
||||||
|
: '例如:您好,我是您的专属AI助手...'
|
||||||
|
}
|
||||||
|
disabled={!isBotFirstTurn || selectedAssistant.generatedOpenerEnabled === true}
|
||||||
className="bg-white/5 border-white/10 focus:border-primary/50 disabled:opacity-50 disabled:cursor-not-allowed"
|
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 className="text-xs text-muted-foreground">
|
||||||
{selectedAssistant.generatedOpenerEnabled === true
|
{!isBotFirstTurn
|
||||||
|
? '已切换为“用户先说”,首轮不会发送开场白。'
|
||||||
|
: selectedAssistant.generatedOpenerEnabled === true
|
||||||
? '通话接通后将根据提示词自动生成开场白。'
|
? '通话接通后将根据提示词自动生成开场白。'
|
||||||
: '接通通话后的第一句话。'}
|
: '接通通话后的第一句话。'}
|
||||||
</p>
|
</p>
|
||||||
@@ -1836,6 +1881,7 @@ export const DebugDrawer: React.FC<{
|
|||||||
mode: ttsEnabled ? 'audio' : 'text',
|
mode: ttsEnabled ? 'audio' : 'text',
|
||||||
},
|
},
|
||||||
systemPrompt: assistant.prompt || '',
|
systemPrompt: assistant.prompt || '',
|
||||||
|
firstTurnMode: assistant.firstTurnMode || 'bot_first',
|
||||||
greeting: assistant.opener || '',
|
greeting: assistant.opener || '',
|
||||||
generatedOpenerEnabled: assistant.generatedOpenerEnabled === true,
|
generatedOpenerEnabled: assistant.generatedOpenerEnabled === true,
|
||||||
bargeIn: {
|
bargeIn: {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const mapAssistant = (raw: AnyRecord): Assistant => ({
|
|||||||
id: String(readField(raw, ['id'], '')),
|
id: String(readField(raw, ['id'], '')),
|
||||||
name: readField(raw, ['name'], ''),
|
name: readField(raw, ['name'], ''),
|
||||||
callCount: Number(readField(raw, ['callCount', 'call_count'], 0)),
|
callCount: Number(readField(raw, ['callCount', 'call_count'], 0)),
|
||||||
|
firstTurnMode: readField(raw, ['firstTurnMode', 'first_turn_mode'], 'bot_first') as 'bot_first' | 'user_first',
|
||||||
opener: readField(raw, ['opener'], ''),
|
opener: readField(raw, ['opener'], ''),
|
||||||
generatedOpenerEnabled: Boolean(readField(raw, ['generatedOpenerEnabled', 'generated_opener_enabled'], false)),
|
generatedOpenerEnabled: Boolean(readField(raw, ['generatedOpenerEnabled', 'generated_opener_enabled'], false)),
|
||||||
prompt: readField(raw, ['prompt'], ''),
|
prompt: readField(raw, ['prompt'], ''),
|
||||||
@@ -213,6 +214,7 @@ export const fetchAssistants = async (): Promise<Assistant[]> => {
|
|||||||
export const createAssistant = async (data: Partial<Assistant>): Promise<Assistant> => {
|
export const createAssistant = async (data: Partial<Assistant>): Promise<Assistant> => {
|
||||||
const payload = {
|
const payload = {
|
||||||
name: data.name || 'New Assistant',
|
name: data.name || 'New Assistant',
|
||||||
|
firstTurnMode: data.firstTurnMode || 'bot_first',
|
||||||
opener: data.opener || '',
|
opener: data.opener || '',
|
||||||
generatedOpenerEnabled: data.generatedOpenerEnabled ?? false,
|
generatedOpenerEnabled: data.generatedOpenerEnabled ?? false,
|
||||||
prompt: data.prompt || '',
|
prompt: data.prompt || '',
|
||||||
@@ -240,6 +242,7 @@ export const createAssistant = async (data: Partial<Assistant>): Promise<Assista
|
|||||||
export const updateAssistant = async (id: string, data: Partial<Assistant>): Promise<Assistant> => {
|
export const updateAssistant = async (id: string, data: Partial<Assistant>): Promise<Assistant> => {
|
||||||
const payload = {
|
const payload = {
|
||||||
name: data.name,
|
name: data.name,
|
||||||
|
firstTurnMode: data.firstTurnMode,
|
||||||
opener: data.opener,
|
opener: data.opener,
|
||||||
generatedOpenerEnabled: data.generatedOpenerEnabled,
|
generatedOpenerEnabled: data.generatedOpenerEnabled,
|
||||||
prompt: data.prompt,
|
prompt: data.prompt,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export interface Assistant {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
callCount: number;
|
callCount: number;
|
||||||
|
firstTurnMode?: 'bot_first' | 'user_first';
|
||||||
opener: string;
|
opener: string;
|
||||||
generatedOpenerEnabled?: boolean;
|
generatedOpenerEnabled?: boolean;
|
||||||
prompt: string;
|
prompt: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user