File size: 3,515 Bytes
2b7b752
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
from dataclasses import dataclass, field
from typing import List, Literal, Optional

from pydantic import BaseModel, Field
from typing_extensions import TypedDict
from langchain.agents import AgentState
from langchain_core.messages import AnyMessage


@dataclass
class AgentContext:
    agent_name: str


@dataclass
class LeadAgentQueryResponse:
    response: str
    language: str
    processed_query: str = None
    confidence_fallback: bool = False
    max_turns_reached: bool = False
    should_cache: bool = False
    appointment_requested: bool = False
    show_booking_widget: bool = False
    relevant_programs: List[str] = field(default_factory=list)


class StructuredAgentResponse(BaseModel):
    response: str = Field(description="Main response to the query.")
    is_context_dependent: bool = Field(
        default=True,
        description=(
            "Set to False only if the question can be answered without using any user-specific "
            "information (e.g. name, age, preferences, extracted profile data) and without relying "
            "on prior conversation turns or conversation history. "
            "Must be True for responses involving eligibility, recommendations, comparisons after prior turns, "
            "or any answer influenced by user profile data or conversation context."
        )
    )
    appointment_requested: bool = Field(
        default=False,
        description="Set to True ONLY if the user explicitly asks to book, schedule, speak with admissions/an advisor, see appointment slots, or accepts a previous consultation offer. Routine pricing, comparisons, recommendations, and exploratory fit questions must be False."
    )
    show_booking_widget: bool = Field(
        default=False,
        description="Set to True ONLY when appointment_requested is True and the booking widget should be shown now. Never use this for soft contact mentions or routine informational answers."
    )
    relevant_programs: Optional[List[Literal["emba", "iemba", "emba_x"]]] = Field(
        default=None,
        description="If appointment_requested is True, list the programs relevant to the user. Options: 'emba', 'iemba', 'emba_x'. If the user is undecided or general, leave this list empty."
    )


class State(TypedDict):
    messages: list[AnyMessage]
    answer: str


class ConversationState(TypedDict):
    """Tracks user profile and conversation context"""
    user_id: str  # Unique session identifier
    user_language: str | None  # Locked after first message
    user_name: str | None  # User's name extracted from conversation
    experience_years: int | None  # Years of professional experience
    leadership_years: int | None  # Years of leadership experience
    field: str | None  # Professional field/industry
    interest: str | None  # Content interests
    qualification_level: str | None  # "bachelor", "master", "MBA", etc.
    program_interest: list[str]  # ["EMBA", "IEMBA", "EMBAX"]
    suggested_program: str | None  # Recommended program based on conversation
    handover_requested: bool | None  # True if appointment requested, False if declined, None if session active
    topics_discussed: list[str]  # Track what's been covered
    preferences_known: bool  # Whether we have enough context


class LeadInformationState(AgentState):
    lead_name: str
    lead_age: int
    lead_language_knowledge: list
    lead_work_experience: dict
    lead_motivation: list
    # Enhanced state tracking
    conversation_state: ConversationState