return random phone number and id card number

This commit is contained in:
Xin Wang 2025-12-16 17:31:17 +08:00
parent e09e4b6930
commit f2fcbe485f
2 changed files with 115 additions and 68 deletions

View File

@ -4,6 +4,7 @@ import base64
import json import json
import logging import logging
import os import os
import random
import sys import sys
import re import re
from dataclasses import asdict, dataclass from dataclasses import asdict, dataclass
@ -104,6 +105,8 @@ DEFAULT_INSTRUCTIONS = """# 角色
- 你已经说过下面的开场白所以不需要重复说您好这里是无锡交警我将为您远程处理交通事故请将人员撤离至路侧安全区域开启危险报警双闪灯放置三角警告牌做好安全防护谨防二次事故伤害若您已经准备好了请点击继续办理如需人工服务请说转人工 - 你已经说过下面的开场白所以不需要重复说您好这里是无锡交警我将为您远程处理交通事故请将人员撤离至路侧安全区域开启危险报警双闪灯放置三角警告牌做好安全防护谨防二次事故伤害若您已经准备好了请点击继续办理如需人工服务请说转人工
""" """
DEFAULT_TALKING_MODE = 'push_to_talk'
# ## 黄金对话路径示例 GOLDEN_CONVERSATION_PATH # ## 黄金对话路径示例 GOLDEN_CONVERSATION_PATH
# ``` # ```
@ -606,11 +609,15 @@ class MyAgent(Agent):
f"│ plate: \"{normalized_plate}\"\n" f"│ plate: \"{normalized_plate}\"\n"
"└───────────────" "└───────────────"
) )
# Dummy fixed response (placeholder backend) # Generate random mobile number (11 digits: 1[3-9] + 9 random digits)
mobile_prefix = random.choice(['13', '14', '15', '16', '17', '18', '19'])
mobile_suffix = ''.join([str(random.randint(0, 9)) for _ in range(9)])
random_mobile = f"{mobile_prefix}{mobile_suffix}"
return { return {
"success": True, "success": True,
"plate": normalized_plate, "plate": normalized_plate,
"mobile": "13800001234", "mobile": random_mobile,
} }
@function_tool() @function_tool()
@ -631,11 +638,24 @@ class MyAgent(Agent):
f"│ plate: \"{normalized_plate}\"\n" f"│ plate: \"{normalized_plate}\"\n"
"└───────────────" "└───────────────"
) )
# Dummy fixed response (placeholder backend) # Generate random ID card number (18 digits: 6-digit area code + 8-digit birth date + 3-digit sequence + 1 check digit)
# Area code: random 6 digits (typically 110000-659999 for Chinese ID cards)
area_code = random.randint(110000, 659999)
# Birth date: random date between 1950-01-01 and 2000-12-31
year = random.randint(1950, 2000)
month = random.randint(1, 12)
day = random.randint(1, 28) # Use 28 to avoid month-specific day issues
birth_date = f"{year:04d}{month:02d}{day:02d}"
# Sequence number: 3 random digits
sequence = random.randint(100, 999)
# Check digit: random digit or X (10% chance of X)
check_digit = 'X' if random.random() < 0.1 else str(random.randint(0, 9))
random_id_card = f"{area_code}{birth_date}{sequence}{check_digit}"
return { return {
"success": True, "success": True,
"plate": normalized_plate, "plate": normalized_plate,
"id_card": "320101198001011234", "id_card": random_id_card,
} }
@function_tool() @function_tool()
@ -749,7 +769,7 @@ class MyAgent(Agent):
raise ToolError(f"Unable to hang up call: {str(e)}") raise ToolError(f"Unable to hang up call: {str(e)}")
@function_tool() @function_tool()
async def ask_important_question(self, context: RunContext, message: str, options: Optional[List[str]] = None): async def ask_important_question(self, context: RunContext, message: str, options: Optional[List[str]] | str = None):
"""询问关键问题并等待用户选择选项,返回用户的选择结果。 """询问关键问题并等待用户选择选项,返回用户的选择结果。
参数: 参数:
@ -759,7 +779,12 @@ class MyAgent(Agent):
返回: 返回:
str: 用户选择的文本内容 str: 用户选择的文本内容
""" """
await self._send_chat_message(f"🔨 Call: ask_important_question\n • message: \"{message}\"\n • options: {options}") await self._send_chat_message(
"┌─🔨 Call: ask_important_question\n"
f"│ message: \"{message}\"\n"
f"│ options: {options}\n"
"└───────────────"
)
try: try:
room = get_job_context().room room = get_job_context().room
participant_identity = next(iter(room.remote_participants)) participant_identity = next(iter(room.remote_participants))
@ -805,7 +830,11 @@ class MyAgent(Agent):
user_selection = response_data.get("selection", "确认") user_selection = response_data.get("selection", "确认")
logger.info(f"User selected: {user_selection}") logger.info(f"User selected: {user_selection}")
await self._send_chat_message(f"✅ Result: ask_important_question\n • selection: \"{user_selection}\"") await self._send_chat_message(
"┌─✅ Result: ask_important_question\n"
f"│ selection: \"{user_selection}\"\n"
"└───────────────"
)
return f"用户选择了: {user_selection}" return f"用户选择了: {user_selection}"
except json.JSONDecodeError: except json.JSONDecodeError:
logger.error(f"Failed to parse response: {response}") logger.error(f"Failed to parse response: {response}")
@ -1015,10 +1044,11 @@ async def entrypoint(ctx: JobContext, avatar_dispatcher_url: str = None, vision_
) )
# disable input audio at the start # disable input audio at the start
session.input.set_audio_enabled(False) _talking_mode = DEFAULT_TALKING_MODE
if _talking_mode == "push_to_talk":
# Track current audio state for mode switching session.input.set_audio_enabled(False)
_audio_enabled_state = False else:
session.input.set_audio_enabled(True)
@ctx.room.local_participant.register_rpc_method("start_turn") @ctx.room.local_participant.register_rpc_method("start_turn")
async def start_turn(data: rtc.RpcInvocationData): async def start_turn(data: rtc.RpcInvocationData):
@ -1058,13 +1088,13 @@ async def entrypoint(ctx: JobContext, avatar_dispatcher_url: str = None, vision_
@ctx.room.local_participant.register_rpc_method("switch_ptt_and_rt") @ctx.room.local_participant.register_rpc_method("switch_ptt_and_rt")
async def switch_ptt_and_rt(data: rtc.RpcInvocationData): async def switch_ptt_and_rt(data: rtc.RpcInvocationData):
nonlocal _audio_enabled_state nonlocal _talking_mode
# Toggle audio input state _talking_mode = "push_to_talk" if _talking_mode == "realtime" else "realtime"
_audio_enabled_state = not _audio_enabled_state if _talking_mode == "push_to_talk":
session.input.set_audio_enabled(_audio_enabled_state) session.input.set_audio_enabled(False)
mode = "push-to-talk" if not _audio_enabled_state else "realtime" else:
logger.info(f"Switched to {mode} mode (audio enabled: {_audio_enabled_state})") session.input.set_audio_enabled(True)
return json.dumps({"success": True, "mode": mode, "audio_enabled": _audio_enabled_state}) return json.dumps({"success": True, "mode": _talking_mode})
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()

View File

@ -1063,61 +1063,66 @@ export function PhoneSimulator({
)} )}
{/* Push-to-Talk Mode Layout */} {/* Push-to-Talk Mode Layout */}
{isPushToTalkMode && phoneMode !== "important_message" && phoneMode !== "hand_off" && voiceAssistant.agent && ( {isPushToTalkMode && phoneMode !== "hand_off" && voiceAssistant.agent && (
<div className="w-full flex items-center justify-center gap-8"> <div className="w-full flex items-center justify-center gap-8">
{/* Camera Switch Button - Left */} {/* Camera Switch Button - Left (hidden in important_message mode) */}
<div className="relative"> {phoneMode !== "important_message" && (
<div className="relative">
<button
className="p-4 rounded-full bg-gray-800/50 text-white hover:bg-gray-800/70 transition-colors"
onClick={handleSwitchCamera}
>
<SwitchCameraIcon className="w-6 h-6" />
</button>
{showCameraMenu && (
<div className="absolute bottom-full mb-2 left-0 bg-gray-900 border border-gray-800 rounded-lg shadow-xl py-2 w-48 z-50">
{cameras.length === 0 ? (
<div className="px-4 py-2 text-gray-500 text-sm">
No cameras found
</div>
) : (
cameras.map((device) => (
<button
key={device.deviceId}
onClick={() => handleSelectCamera(device.deviceId)}
className="w-full text-left px-4 py-2 text-sm text-white hover:bg-gray-800 transition-colors truncate"
>
{device.label ||
`Camera ${cameras.indexOf(device) + 1}`}
</button>
))
)}
</div>
)}
</div>
)}
{/* Large Push-to-Talk Button - Center (hidden in important_message mode) */}
{phoneMode !== "important_message" && (
<button <button
className="p-4 rounded-full bg-gray-800/50 text-white hover:bg-gray-800/70 transition-colors" ref={pushToTalkButtonRef}
onClick={handleSwitchCamera} className={`w-24 h-24 rounded-full backdrop-blur-md transition-all flex flex-col items-center justify-center gap-2 aspect-square ${
interruptRejected
? "bg-red-500/70 text-white"
: isPushToTalkActive
? "bg-green-500 text-white scale-110 shadow-lg shadow-green-500/50"
: "bg-blue-500/70 text-white hover:bg-blue-500/90"
}`}
style={{ borderRadius: '50%' }}
onMouseDown={handlePushToTalkMouseDown}
onMouseUp={handlePushToTalkMouseUp}
onTouchStart={handlePushToTalkTouchStart}
onTouchEnd={handlePushToTalkTouchEnd}
title={supportsPushToTalk ? "Push to Talk" : "Push to Talk (may not be supported by this agent)"}
> >
<SwitchCameraIcon className="w-6 h-6" /> <MicIcon className="w-8 h-8" />
<span className="text-xs font-medium">
{interruptRejected ? "不允许打断" : "按住说话"}
</span>
</button> </button>
{showCameraMenu && ( )}
<div className="absolute bottom-full mb-2 left-0 bg-gray-900 border border-gray-800 rounded-lg shadow-xl py-2 w-48 z-50">
{cameras.length === 0 ? (
<div className="px-4 py-2 text-gray-500 text-sm">
No cameras found
</div>
) : (
cameras.map((device) => (
<button
key={device.deviceId}
onClick={() => handleSelectCamera(device.deviceId)}
className="w-full text-left px-4 py-2 text-sm text-white hover:bg-gray-800 transition-colors truncate"
>
{device.label ||
`Camera ${cameras.indexOf(device) + 1}`}
</button>
))
)}
</div>
)}
</div>
{/* Large Push-to-Talk Button - Center */} {/* End Call Button - Right (always shown in PTT mode) */}
<button
ref={pushToTalkButtonRef}
className={`w-24 h-24 rounded-full backdrop-blur-md transition-all flex flex-col items-center justify-center gap-2 ${
interruptRejected
? "bg-red-500/70 text-white"
: isPushToTalkActive
? "bg-green-500 text-white scale-110 shadow-lg shadow-green-500/50"
: "bg-blue-500/70 text-white hover:bg-blue-500/90"
}`}
onMouseDown={handlePushToTalkMouseDown}
onMouseUp={handlePushToTalkMouseUp}
onTouchStart={handlePushToTalkTouchStart}
onTouchEnd={handlePushToTalkTouchEnd}
title={supportsPushToTalk ? "Push to Talk" : "Push to Talk (may not be supported by this agent)"}
>
<MicIcon className="w-8 h-8" />
<span className="text-xs font-medium">
{interruptRejected ? "不允许打断" : "按住说话"}
</span>
</button>
{/* End Call Button - Right */}
<button <button
className="p-4 rounded-full bg-red-500 text-white hover:bg-red-600 transition-colors" className="p-4 rounded-full bg-red-500 text-white hover:bg-red-600 transition-colors"
onClick={handleDisconnect} onClick={handleDisconnect}
@ -1156,6 +1161,18 @@ export function PhoneSimulator({
</button> </button>
</div> </div>
)} )}
{/* Hand Off Mode - Show only End Call Button */}
{phoneMode === "hand_off" && (
<div className="w-full flex items-center justify-center">
<button
className="p-4 rounded-full bg-red-500 text-white hover:bg-red-600 transition-colors"
onClick={handleDisconnect}
>
<PhoneOffIcon className="w-6 h-6" />
</button>
</div>
)}
</div> </div>
</div> </div>
) )