| import threading |
| from abc import ABC |
| from typing import Dict, Optional |
|
|
| from loguru import logger |
| from pydantic import BaseModel, Field |
|
|
| from chat_engine.contexts.handler_context import HandlerContext |
| from chat_engine.data_models.chat_engine_config_data import ChatEngineConfigModel, HandlerBaseConfigModel |
| from chat_engine.common.handler_base import HandlerBase, HandlerBaseInfo, HandlerDataInfo, HandlerDetail |
| from chat_engine.data_models.chat_data.chat_data_model import ChatData |
| from chat_engine.data_models.chat_data_type import ChatDataType |
| from chat_engine.data_models.runtime_data.data_bundle import DataBundle, DataBundleDefinition, DataBundleEntry |
| from chat_engine.contexts.session_context import SessionContext |
|
|
|
|
| class GreeterConfig(HandlerBaseConfigModel, BaseModel): |
| greeting_prompt: str = Field(default="Introduce yourself to the visitor now, in your own words.") |
| delay_seconds: float = Field(default=4.0) |
|
|
|
|
| class GreeterContext(HandlerContext): |
| def __init__(self, session_id: str): |
| super().__init__(session_id) |
| self.timer = None |
| self.sent = False |
|
|
|
|
| class HandlerGreeter(HandlerBase, ABC): |
| """On session start, injects one synthetic HUMAN_TEXT turn so the |
| avatar proactively greets the user before any mic input arrives.""" |
|
|
| def __init__(self): |
| super().__init__() |
| self.greeting_prompt = "Introduce yourself to the visitor now, in your own words." |
| self.delay_seconds = 4.0 |
| self.output_definition = None |
|
|
| def get_handler_info(self) -> HandlerBaseInfo: |
| return HandlerBaseInfo( |
| name="Greeter", |
| config_model=GreeterConfig, |
| ) |
|
|
| def load(self, engine_config: ChatEngineConfigModel, handler_config: Optional[BaseModel] = None): |
| if isinstance(handler_config, GreeterConfig): |
| self.greeting_prompt = handler_config.greeting_prompt |
| self.delay_seconds = handler_config.delay_seconds |
| logger.info(f"Greeter loaded: delay={self.delay_seconds}s prompt={self.greeting_prompt!r}") |
|
|
| def get_handler_detail(self, session_context: SessionContext, |
| context: HandlerContext) -> HandlerDetail: |
| definition = DataBundleDefinition() |
| definition.add_entry(DataBundleEntry.create_audio_entry("avatar_audio", 1, 24000)) |
| self.output_definition = definition |
| |
| |
| inputs = { |
| ChatDataType.AVATAR_TEXT: HandlerDataInfo( |
| type=ChatDataType.AVATAR_TEXT, |
| ) |
| } |
| outputs = { |
| ChatDataType.HUMAN_TEXT: HandlerDataInfo( |
| type=ChatDataType.HUMAN_TEXT, |
| definition=definition, |
| ) |
| } |
| return HandlerDetail(inputs=inputs, outputs=outputs) |
|
|
| def create_context(self, session_context, handler_config=None): |
| return GreeterContext(session_context.session_info.session_id) |
|
|
| def start_context(self, session_context, handler_context): |
| context = handler_context |
| if not isinstance(context, GreeterContext): |
| return |
|
|
| def _send_greeting(): |
| if context.sent: |
| return |
| context.sent = True |
| try: |
| output = DataBundle(self.output_definition) |
| output.set_main_data(self.greeting_prompt) |
| context.submit_data(output, finish_stream=True) |
| logger.info(f"Greeter: injected greeting turn for session {context.session_id}") |
| except Exception as e: |
| logger.error(f"Greeter failed to inject greeting: {e}") |
|
|
| context.timer = threading.Timer(self.delay_seconds, _send_greeting) |
| context.timer.daemon = True |
| context.timer.start() |
|
|
| def handle(self, context: HandlerContext, inputs: ChatData, |
| output_definitions: Dict[ChatDataType, HandlerDataInfo]): |
| |
| return |
|
|
| def destroy_context(self, context: HandlerContext): |
| if isinstance(context, GreeterContext) and context.timer is not None: |
| context.timer.cancel() |
|
|