File size: 31,171 Bytes
1e93383
 
ba37068
 
 
 
 
 
 
 
 
 
 
 
1e93383
c65610e
ba37068
c65610e
 
 
 
 
 
ba37068
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c65610e
1e93383
ba37068
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1e93383
c65610e
 
ba37068
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
import os
import gradio as gr
from openai import AsyncAzureOpenAI,AzureOpenAI
from pydantic import BaseModel, Field
from typing import List, Optional
import asyncio # Required for async Gradio functions


class InterviewStep(BaseModel):
    reason_replanning: List[str] = Field(description="Should we replan short term or long term? If so, why? If not, why not?")
    short_term_plan: str = Field(description="Detailed plan for the following turns of the conversation. This should change based on the employee's responses, particularly when new, relevant things are mentioned.")
    long_term_plan: str = Field(description="Detailed long term plan for the remaining part of the conversation. Be specific and detailed.")
    reasoning: List[str] = Field(description="The reasoning for your responses. Could be a multi-step reasoning.")
    response_for_employee: str = Field(description="The response you will give to the employee.")

# Initialize Azure OpenAI client
def get_azure_client():
    return AzureOpenAI(
        api_key=os.getenv("AZURE_KEY"),
        api_version=os.getenv("AZURE_VERSION"),
        azure_endpoint=os.getenv("AZURE_URL")
    )

# --- System Prompt Template (adapted from notebook) ---
# This is the core instruction set for the AI interviewer.
SYS_PROMPT_TEMPLATE = """
**You are Nico, the Horizon Copilot—an AI digital transformation consultant. Your role is to conduct insightful, adaptive interviews with employees of a company, aiming to gather valuable information for the company's digital transformation journey.**
### **Interview Context:**
-   **Employee Information:** Name, role, area, and any other relevant details.
-   **Company Information:** Name, industry, size, objectives, strategy, and the purpose of the consulting discovery process.
-   **Process Framework:** An APQC-style matrix outlining the expected processes and activities in the employee's area.
-   **Additional Guidelines:** Any other objectives, focus areas, or key insights we want to uncover.
### **Interview Objective:**
Your main objective is to conduct a natural, formal, and engaging consulting interview. You will explore the employee's understanding of processes in their area, identify pain points, improvement opportunities, and any other relevant insights.
### **Interview Strategy:**
-   **Adaptive Approach:** Use a semi-structured plan based on the APQC matrix, but adapt your questions based on the employee's responses. Avoid pursuing irrelevant topics.
-   **Core Coverage:** Ensure you address the key points of the matrix but feel free to explore new insights if the employee shares valuable information.
-   **Dynamic Management:** After each employee response, reassess your strategy to ensure the conversation remains valuable and on track.
-   **Conversation Limits:** Aim for a soft maximum of 20 turns. You will be informed of the current turn count.
-   **Ending Gracefully:** When the conversation has reached its natural end, thank the employee and politely close the interview.
-   **Natural flow:** The conversation should be natural and fluid, with the employee feeling comfortable and at ease. This is a conversation, not a scripted interview nor a survey.
-   **Questions per message:** You should only make 1 question per turn unless certain specific cases where you feel it's necessary to include more in order to better extract the info from the employee.
### **Adaptive Planning:**
After each employee response:
-   Reassess your current plan.
-   Adjust your next question or focus based on the new information.
-   Prioritize clarity, relevance, and engagement.
---Context for this interview---
Here is the current context for this particular interview you will be carrying out:
{employee_data_block}
{company_data_block}
{apqc_matrix_block}
<InitialObjective>
-   **Inicio y encuadre rápido**
    -   Preséntate y explica brevemente el propósito:
        > "... mi idea es poder entender como llevas a cabo tu las tareas del dia a dia y como hacen las cosas dentro del area para identificar si hay algunas posibles mejoras en los procesos para poder hacer tu dia a dia mas facil."
    -   Aclara la dinámica: preguntas abiertas, posibilidad de profundizar, que pregunte si no entiende algo.
-   **Entendimiento de las filas**
    Para las distintas filas de la matriz (actividad) vamos a buscar entender algunas cosas (segun corresponda para cada actividad):
    1.  **Pertinencia y contexto**
        -   Ya sea intuyendolo, o preguntando al empleado por como hace la actividad identificamos si es pertinente a la entrevista:
            > Eg: "Recibes solicitudes de otros empleados? Como las recibis?"
        -   **Si no aplica**, cerramos:
            > "Perfecto, entonces sigamos."
        -   **Si aplica**, avanzamos.
        -   "¿Qué triggerea normalmente esta actividad?"
        -   "¿En qué momento del día o del ticket aparece?"
    2.  **Flujo y herramientas**
        -   "¿Cómo lo hacés paso a paso?"
        -   "¿Con qué sistemas o plataformas (ITSM, Slack, scripts...) trabajas aquí?"
    3.  **Volumen, salidas y metricas**
        -   "¿Con qué frecuencia la ejecutas?"
        -   "¿Cuántos tickets/casos manejas en un mes típico?"
        -   "¿Qué registros o reportes generás?"
        -   "¿Cómo sabés si lo estás haciendo bien? ¿Qué indicadores seguís?"
    4.  **Fricciones y mejoras** - Las mejoras no son parte de los building blocks
        -   "¿Qué parte de este flujo te consume más tiempo o te genera más errores?"
        -   "¿Has tenido que recurrir a soluciones improvisadas?"
        -   "Si pudieras cambiar o automatizar un paso acá, ¿cuál sería?"
        -   "¿Hay ideas que ya hayan surgido en tu equipo?"
    8.  **Mini-resumen**
        > Entonces, para **[actividad]**, vos la iniciás cuando ..., usás ..., la hacés ... veces/mes, tu mayor desafío es ... y lo medís con ... ¿Falta algo?
-   **Gestión de excepciones y agrupamientos**
    -   Si el entrevistado agrupa varias filas en una explicación, reconoce el bundle y adapta:
        > "Entiendo que entre X e Y vos hacés un flujo combinado. Vamos a profundizar en ese flujo conjunto..."
    -   Si surge un sub-proceso nuevo o una excepción no prevista, inclúyelo en tu bitácora y hazle un mini-bloque:
        > "Contame más de ese caso especial cuando...".
-   **Cierre global**
    -   Al haber analizado las distintas actividades podemos explicar el entendimiento que tenemos de su trabajo y preguntar si falta algo:
        > "De mi lado tengo un entendimiento claro de lo que serian tus tareas dentro del area. Entiendo que haces (resumen). ¿Hay alguna actividad o excepción que no hayamos hablado y vos no realices?"
Es importante no ver a esto como un checklist rigido, si no una mini guia de algunos casos que pueden aparecer.
</InitialObjective>
"""

SYS_PROMPT_TEMPLATE = """
You are Nico, the Horizon Copilot—an AI digital transformation consultant. Your role is to conduct insightful, adaptive interviews with employees of a company, aiming to gather valuable information for the company's digital transformation journey.
### Interview Context:
-   **Employee Information:** Name, role, area, and any other relevant details.
    
-   **Company Information:** Name, industry, size, objectives, strategy, and the purpose of the consulting discovery process.
    
-   **Process Framework:** An APQC-style matrix outlining the expected processes and activities in the employee's area.
    
-   **Additional Guidelines:** Any other objectives, focus areas, or key insights we want to uncover.
    
### Interview Objective:
Your main objective is to conduct a natural, formal, and engaging consulting interview. You will explore the employee's understanding of processes in their area, identify pain points, improvement opportunities, and any other relevant insights.
### Interview Strategy:
-   Adaptive Approach: Use a semi-structured plan based on the APQC matrix, but adapt your questions based on the employee's responses. Avoid pursuing irrelevant topics.
    
-   Core Coverage: Ensure you address the key points of the matrix but feel free to explore new insights if the employee shares valuable information.
    
-   Dynamic Management: After each employee response, reassess your strategy to ensure the conversation remains valuable and on track.
    
-   Conversation Limits: Aim for a soft maximum of 20 turns. You will be informed of the current turn count.
    
-   Ending Gracefully: When the conversation has reached its natural end, thank the employee and politely close the interview.
-   Natural flow: The conversation should be natural and fluid, with the employee feeling comfortable and at ease. This is a conversation, not a scripted interview nor a survey. Show attention to the employee's responses.
-   Factual information: We are looking for factual information. It is ok for you to sometimes ask for clarifications or ask for some probing on topics.
-   Questions per message: You should only make 1 question per turn unless certain specific cases where you feel it's necessary to include more in order to better extract the info from the employee.
    
### **Adaptive Planning:**
After each employee response:
-   Reassess your current plan.
    
-   Adjust your next question or focus based on the new information.
    
-   Prioritize clarity, relevance, and engagement.
---Context for this interview---
Here is the current context for this particular interview you will be carrying out:
{employee_data_block}
{company_data_block}
{apqc_matrix_block}
{initial_objective_block}
"""

# --- Helper functions to format data for the system prompt ---
def format_employee_data(name, role, area):
    return f"<EmployeeData>\nName: {name}\nRole: {role}\nArea: {area}\n</EmployeeData>"

def format_company_data(company_info):
    return f"<CompanyData>\n{company_info}\n</CompanyData>"

def format_apqc_matrix(apqc_info):
    return f"<APQCMatrixForArea>\n{apqc_info}\n</APQCMatrixForArea>"

def format_initial_objective(initial_objective):
    return f"<InitialObjective>\n{initial_objective}\n</InitialObjective>"

# --- Gradio Callbacks ---
async def start_interview(emp_name, emp_role, emp_area, company_info, apqc_info, initial_objective):
    """
    Initializes the interview by sending the setup information to the AI 
    and getting the first question.
    """
    azure_client = get_azure_client()
    messages_list_for_llm_history = [] 
    turn_count = 0
    
    employee_data_str = format_employee_data(emp_name, emp_role, emp_area)
    company_data_str = format_company_data(company_info)
    apqc_matrix_str = format_apqc_matrix(apqc_info)
    initial_objective_str = format_initial_objective(initial_objective)

    current_sys_prompt = SYS_PROMPT_TEMPLATE.format(
        employee_data_block=employee_data_str,
        company_data_block=company_data_str,
        apqc_matrix_block=apqc_matrix_str,
        initial_objective_block=initial_objective_str
    )
    
    # Messages for the first API call
    messages_for_api_call = [{"role": "system", "content": current_sys_prompt}]
    
    initial_user_directive = "Generate your plans and start the conversation"
    messages_for_api_call.append({"role": "user", "content": initial_user_directive})
    
    messages_list_for_llm_history.append({"role": "user", "content": initial_user_directive})

    try:
        response = azure_client.beta.chat.completions.parse(
            model="gpt-4.1",
            messages=messages_for_api_call,
            response_format=InterviewStep,
            temperature=0.3
        )
        interview_step: InterviewStep = response.choices[0].message.parsed
        
        # Store AI's first structured response (including the question) in history
        messages_list_for_llm_history.append({"role": "assistant", **interview_step.model_dump()})
        
        # Initial chat history for display in Gradio: AI's first question
        chat_history_display = [("", interview_step.response_for_employee)] # (User, AI)
        
        return (
            chat_history_display, 
            messages_list_for_llm_history, 
            turn_count + 1,
            current_sys_prompt,
            "\n".join(interview_step.reasoning),
            "\n".join(interview_step.reason_replanning),
            interview_step.short_term_plan or "N/A",
            interview_step.long_term_plan or "N/A",
            gr.update(visible=False), # For setup_column
            gr.update(visible=True)   # For chat_column
        )
    except Exception as e:
        error_message = f"Error starting interview: {str(e)}"
        print(error_message)
        # Return an error message in the chat and reasoning display
        return (
            [("", error_message)], 
            [], 
            0, 
            current_sys_prompt, 
            error_message, 
            "", 
            "", 
            "",
            gr.update(visible=True), # Keep setup_column visible
            gr.update(visible=True)  # Keep chat_column visible
        )


async def handle_employee_response(
    employee_response_text: str, 
    chat_history_display_input: list, 
    messages_list_for_llm_history_input: list, 
    current_turn_count: int,
    current_sys_prompt: str,
    setup_column, # Add setup_column to outputs
    chat_column   # Add chat_column to outputs
    ):
    """
    Handles the employee's response, sends it to the AI, and gets the next question/plan.
    """
    azure_client = get_azure_client()
    if not employee_response_text.strip(): # Ignore empty submissions
        # Return current state without changes, keep employee_response_text in box if it was just whitespace
        return (
            chat_history_display_input, 
            messages_list_for_llm_history_input, 
            current_turn_count, 
            gr.update(), gr.update(), gr.update(), gr.update(), 
            employee_response_text,
            setup_column, # Keep setup_column visible
            chat_column   # Keep chat_column visible
        )

    # Make copies of state lists to avoid modifying them directly if an error occurs mid-way
    chat_history_display = list(chat_history_display_input)
    messages_list_for_llm_history = list(messages_list_for_llm_history_input)

    # Construct messages for the API call from the history
    messages_for_api_call = [{"role": "system", "content": current_sys_prompt}]
    
    for message_item in messages_list_for_llm_history:
        if message_item["role"] == "assistant":
            # Format assistant's previous turn (plan + question)
            formatted_assistant_msg = (
                f'short_term_plan="{message_item.get("short_term_plan", "")}"\n'
                f'long_term_plan="{message_item.get("long_term_plan", "")}"\n\n'
                f'question_for_employee="{message_item.get("response_for_employee", "")}"'
            )
            messages_for_api_call.append({"role": "assistant", "content": formatted_assistant_msg})
        else: # "user" role
            messages_for_api_call.append({"role": "user", "content": message_item["content"]})
            
    # Add current employee's response, formatted for the AI
    current_user_message_formatted = f"Turn count: {current_turn_count}\nResponse from the employee:\n{employee_response_text}"
    messages_for_api_call.append({"role": "user", "content": current_user_message_formatted})
    
    # Add the raw employee response to the history list (for future reconstruction)
    messages_list_for_llm_history.append({"role": "user", "content": employee_response_text})

    try:
        response = azure_client.beta.chat.completions.parse(
            model="gpt-4.1", # Deployment name
            messages=messages_for_api_call,
            response_format=InterviewStep,
            temperature=0.3
        )
        interview_step: InterviewStep = response.choices[0].message.parsed
        
        # Add AI's new structured response to history
        messages_list_for_llm_history.append({"role": "assistant", **interview_step.model_dump()})
        
        # Update chat history for Gradio display
        chat_history_display.append((employee_response_text, interview_step.response_for_employee))
        
        return (
            chat_history_display, 
            messages_list_for_llm_history, 
            current_turn_count + 1,
            "\n".join(interview_step.reasoning),
            "\n".join(interview_step.reason_replanning),
            interview_step.short_term_plan or "N/A",
            interview_step.long_term_plan or "N/A",
            gr.update(visible=False), # Hide setup_column
            gr.update(visible=True)   # Ensure chat_column is visible (and will expand)
        )
    except Exception as e:
        error_message = f"Error processing response: {str(e)}"
        print(error_message)
        chat_history_display.append((employee_response_text, error_message)) # Show error in chat
        # Return state mostly as it was before this failed call, but show error.
        # messages_list_for_llm_history_input does not include the latest AI response that failed.
        # It does include the last user message.
        return (
            chat_history_display, 
            messages_list_for_llm_history, # This version includes the user's last message
            current_turn_count, # Don't advance turn count
            error_message, "", "", "", 
            employee_response_text, # Keep user input in the box
            setup_column, # Keep setup_column visible
            chat_column   # Keep chat_column visible
        )

# --- Default values for UI (from notebook's sys_prompt context) ---
DEFAULT_EMP_NAME = "Matias Silva"
DEFAULT_EMP_ROLE = "Internal Mesa de Ayuda"
DEFAULT_EMP_AREA = "Operations"
DEFAULT_COMPANY_INFO = "Montes del Plata es la principal planta de celulosa de Uruguay, con capacidad de producción de pulpa de eucalipto de alrededor de 1,4 millones de toneladas al año, además de generar internamente su energía a partir de biomasa, aportando cerca del 9 % del suministro eléctrico nacional. Gestiona unas 190 000 ha de bosques certificados FSC y PEFC, emplea alrededor de 630 personas directamente y moviliza más de 6 500 empleos directos e indirectos en su cadena de valor. Inaugurado en 2014, su complejo industrial integra un sistema logístico bimodal (terrestre y fluvial) y opera bajo los estándares de mejores técnicas disponibles de la UE (IPPC-BAT)."
DEFAULT_APQC_MATRIX = """
| Nivel 1: Macroproceso                           | Nivel 2: Subproceso                                    | Nivel 3: Actividad Detallada                            | Nivel 4: Procedimiento / Workflow                                              | Nivel 5: Instrucción de Trabajo ideal / Configuración Técnica                                 |
|-------------------------------------------------|--------------------------------------------------------|---------------------------------------------------------|----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|
| Soporte IT Interno (Mesa de Ayuda)              | 1.1 Recepción y Registro de Solicitudes                | 1.1.1 Recepción de solicitudes multicanal               | Registrar automáticamente en herramienta ITSM                                     | Configurar canales de entrada (portal, email, teléfono)                                  |
|                                                 |                                                        | 1.1.2 Clasificación y categorización de tickets         | Aplicar reglas de categorización según tipo de incidente                          | Motor de reglas en ITSM, aprendizaje automático supervisado                              |
|                                                 |                                                        | 1.1.3 Asignación y ruteo                                | Asignar automáticamente a grupo de soporte correspondiente                        | Configuración de colas por especialidad, turnos y SLAs                                   |
|                                                 | 1.2 Gestión de Incidentes                              | 1.2.1 Diagnóstico inicial                               | Ejecutar troubleshooting básico y consulta de KB                                  | Checklist de diagnóstico, acceso a base de conocimiento                                 |
|                                                 |                                                        | 1.2.2 Resolución o escalamiento                         | Aplicar solución o derivar según criticidad                                       | Guías de resolución rápida, matriz de escalamiento definida                             |
|                                                 |                                                        | 1.2.3 Cierre de ticket con documentación                | Registrar causa raíz y solución aplicada                                         | Plantilla de cierre, actualización automática de KB si aplica                           |
|                                                 | 1.3 Gestión de Solicitudes de Servicio                 | 1.3.1 Alta/Baja/Modificación de accesos y permisos      | Ejecutar workflows aprobados y registrar cambios                                   | Integración con sistemas de identidades y flujos de autorización                        |
|                                                 |                                                        | 1.3.2 Instalación o configuración de software           | Validar requerimiento y aplicar política de instalación                            | Uso de herramientas de despliegue remoto (SCCM, Intune, etc.)                           |
|                                                 | 1.4 Gestión del Conocimiento                           | 1.4.1 Generación y mantenimiento de artículos KB        | Documentar casos recurrentes                                                      | Formato estandarizado; revisión periódica asignada                                      |
|                                                 |                                                        | 1.4.2 Uso activo de KB por agentes                      | Consultar y vincular KB al ticket                                                 | Integración nativa KB en plataforma ITSM                                                |
|                                                 | 1.5 Monitoreo, Reportes y Mejora Continua              | 1.5.1 Seguimiento de SLA y métricas operativas          | Generar reportes mensuales de desempeño                                           | Dashboards en Power BI, ServiceNow o Freshservice                                       |
|                                                 |                                                        | 1.5.2 Revisión de feedback y encuestas                  | Analizar CSAT post-cierre                                                         | Encuestas automáticas y análisis mensual                                                |
|                                                 |                                                        | 1.5.3 Identificación de automatizaciones                | Detectar tickets repetitivos                                                      | Análisis de tendencias; propuestas de RPA o self-service                                |
"""
DEFAULT_INITIAL_OBJECTIVE = """
### Inicio y encuadre rápido
- Preséntate y explica brevemente el propósito:
> "... mi idea es poder entender como llevas a cabo tu las tareas del dia a dia y como hacen las cosas dentro del area para identificar si hay algunas posibles mejoras en los procesos para poder hacer tu dia a dia mas facil."
        
 -   Aclara la dinámica: preguntas abiertas, posibilidad de profundizar, que pregunte si no entiende algo.
   
   - Comienza preguntando sobre sus tareas del dia a dia para abrir la conversacion. En base a esa respuesta, pasamos al entendimiento de la matriz acordemente.
        
### Entendimiento de las filas
Para las distintas filas de la matriz (actividad) vamos a buscar entender algunas cosas (segun corresponda para cada actividad):
    
1.  **Pertinencia y contexto**
        
-   Ya sea intuyendolo, o preguntando al empleado identificamos si es pertinente a la entrevista:
	Eg: "Recibes solicitudes de otros empleados? Como las recibis?"
-   **Si no aplica**, cerramos:
	"Perfecto, entonces sigamos."
-   **Si aplica**, avanzamos.
	-   "¿Qué triggerea normalmente esta actividad?"
	-   "¿En qué momento del día o del ticket aparece?"
            
2.  **Flujo y herramientas**
-   "¿Cómo lo hacés paso a paso?"
-   "¿Con qué sistemas o plataformas (ITSM, Slack, scripts...) trabajas aquí?"
            
3.  **Volumen, salidas y metricas**
-   "¿Con qué frecuencia la ejecutas?"
-   "¿Cuántos tickets/casos manejas en un mes típico?"
-   "¿Qué registros o reportes generás?"
-   "¿Cómo sabés si lo estás haciendo bien? ¿Qué indicadores seguís?"
            
4.  **Fricciones y mejoras** - Las mejoras no son parte de los building blocks
-   "¿Qué parte de este flujo te consume más tiempo o te genera más errores?"
-   "¿Has tenido que recurrir a soluciones improvisadas?"
-   "Si pudieras cambiar o automatizar un paso acá, ¿cuál sería?"
-   "¿Hay ideas que ya hayan surgido en tu equipo?"
            
5.  **Mini-resumen**
Una vez relevada una actividad, proceso o sub proceso podemos pedir confirmacion de algunos puntos.
Eg: "Entonces, para **[actividad]**, vos la iniciás cuando **...**, usás **...**, la hacés **... veces/mes**, tu mayor desafío es **...** y lo medís con **...**. ¿Falta algo?"
            
### Gestión de excepciones y agrupamientos
-   Si el entrevistado agrupa varias filas en una explicación, reconoce el bundle y adapta:
> "Entiendo que entre X e Y vos hacés un flujo combinado. Vamos a profundizar en ese flujo conjunto..."
-   Si surge un sub-proceso nuevo o una excepción no prevista, inclúyelo en tu planificacoin y hazle un mini-bloque:
> "Contame más de ese caso especial cuando...".
        
## Cierre global
    
-   Al haber analizado las distintas actividades podemos explicar el entendimiento que tenemos de su trabajo y preguntar si falta algo:
> "De mi lado tengo un entendimiento claro de lo que serian tus tareas dentro del area. Entiendo que haces (resumen). ¿Hay alguna actividad o excepción que no hayamos hablado y vos no realices?"
Es importante no ver a esto como un checklist rigido, si no una mini guia de algunos casos que pueden aparecer.
"""

# --- Gradio UI Layout ---
with gr.Blocks(theme=gr.themes.Soft(), title="AI Interviewer") as demo:
    gr.Markdown("# AI Interviewer (Nico, the Horizon Copilot)")
    gr.Markdown("This application simulates an AI interviewer (Nico) who will ask you questions based on the provided context. You play the role of the employee being interviewed.")

    # --- Persistent State Variables for the Session ---
    messages_list_state = gr.State([]) 
    turn_count_state = gr.State(0)
    system_prompt_state = gr.State("") 

    with gr.Row():
        setup_column = gr.Column(scale=1, visible=True)
        chat_column = gr.Column(scale=2, visible=True)

        with setup_column:
            gr.Markdown("## Interview Setup")
            gr.Markdown("Provide the context for the interview. Nico will use this to tailor the conversation.")
            emp_name_input = gr.Textbox(label="Employee Name", value=DEFAULT_EMP_NAME)
            emp_role_input = gr.Textbox(label="Employee Role", value=DEFAULT_EMP_ROLE)
            emp_area_input = gr.Textbox(label="Employee Area/Department", value=DEFAULT_EMP_AREA)
            company_info_input = gr.Textbox(label="Company Information", lines=7, value=DEFAULT_COMPANY_INFO,elem_id="company-info")
            apqc_matrix_input = gr.Textbox(label="Process Framework (e.g., APQC Matrix for Employee's Area)", lines=10, value=DEFAULT_APQC_MATRIX, elem_id="apqc-matrix")
            initial_objective_input = gr.Textbox(label="Initial Objective", lines=7, value=DEFAULT_INITIAL_OBJECTIVE, elem_id="initial-objective")
            start_button = gr.Button("Start Interview with Nico", variant="primary")
            
        with chat_column:
            gr.Markdown("## Interview Chat with Nico")
            with gr.Row():
                with gr.Column(scale=2):
                    chatbot_display = gr.Chatbot(label="Nico (AI Interviewer)", height=550, bubble_full_width=False, avatar_images=(None, "https://cdn-icons-png.flaticon.com/512/1698/1698535.png"))
                    employee_response_box = gr.Textbox(label="Your Response (as Employee)", placeholder="Type your response here and press Enter to submit...", show_label=True)
                
                with gr.Column(scale=1):
                    gr.Markdown("### AI's Internal Thoughts")
                    reasoning_display = gr.Textbox(label="Nico's Reasoning for the question:", lines=10, interactive=False)
                    reason_replanning_display = gr.Textbox(label="Nico's Reason for Replanning (if any):", lines=5, interactive=False)
                    short_term_plan_display = gr.Textbox(label="Nico's Short-Term Plan:", lines=5, interactive=False)
                    long_term_plan_display = gr.Textbox(label="Nico's Long-Term Plan:", lines=7, interactive=False)

    # --- Event Handlers Wiring ---
    start_button.click(
        fn=start_interview,
        inputs=[emp_name_input, emp_role_input, emp_area_input, company_info_input, apqc_matrix_input, initial_objective_input],
        outputs=[
            chatbot_display, 
            messages_list_state, 
            turn_count_state, 
            system_prompt_state,
            reasoning_display, 
            reason_replanning_display, 
            short_term_plan_display, 
            long_term_plan_display,
            setup_column,  # Controls visibility of the setup column
            chat_column    # Controls visibility of the main chat/reasoning column
        ],
        api_name="start_interview" 
    )

    employee_response_box.submit(
        fn=handle_employee_response,
        inputs=[
            employee_response_box, 
            chatbot_display, 
            messages_list_state, 
            turn_count_state,
            system_prompt_state
        ],
        outputs=[
            chatbot_display, 
            messages_list_state, 
            turn_count_state,
            reasoning_display, 
            reason_replanning_display, 
            short_term_plan_display, 
            long_term_plan_display,
            employee_response_box 
        ],
        api_name="employee_response" 
    )

if __name__ == "__main__":
    demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))