gerardocabrera commited on
Commit
44bf44b
·
1 Parent(s): 7f02846

Modificación de proceso de creación de tickets

Browse files
Files changed (1) hide show
  1. app.py +184 -55
app.py CHANGED
@@ -1,11 +1,20 @@
 
 
 
 
 
 
 
1
  import pandas as pd
2
  import torch
 
3
  from transformers import pipeline, AutoTokenizer
4
- import re
5
- import gradio as gr
6
 
7
- # 1. Configuración optimizada
8
- MODEL_NAME = "MoritzLaurer/mDeBERTa-v3-base-xnli-anli"
 
 
 
9
  CATEGORIES = ["logística", "pagos", "producto defectuoso", "cuenta", "facturación", "otros"]
10
  URGENCY_PATTERNS = [
11
  r"\b(urgente|inmediato|cr[íi]tico|asap)\b",
@@ -14,79 +23,199 @@ URGENCY_PATTERNS = [
14
  r"\b(prioridad [1-3]|nivel [1-3])\b"
15
  ]
16
 
17
- # 2. Cargar modelo solo una vez
18
- tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
19
- classifier = pipeline(
20
- "zero-shot-classification",
21
- model=MODEL_NAME,
22
- tokenizer=tokenizer,
23
- device=0 if torch.cuda.is_available() else -1,
24
- torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32
25
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- # 3. Detección de urgencia
28
  def is_urgent(text: str) -> bool:
29
  text = text.lower()
30
  if any(re.search(pattern, text, flags=re.IGNORECASE) for pattern in URGENCY_PATTERNS):
31
  return True
32
-
33
- # Heurística semántica (solo si no se detectó con regex)
34
- urgency_result = classifier(text, ["urgente", "no urgente"], multi_label=False)
35
- return urgency_result['labels'][0] == "urgente" and urgency_result['scores'][0] > 0.85
36
 
37
- # 4. Clasificación de categorías
38
  def classify_text(text: str) -> str:
 
 
39
  result = classifier(text, CATEGORIES, multi_label=False)
40
  return result['labels'][0]
41
 
42
- # 5. Función para la interfaz Gradio
43
- def classify_ticket(text):
 
 
 
44
  if not text.strip():
45
- return "", ""
46
 
47
  category = classify_text(text)
48
- urgent = "URGENTE" if is_urgent(text) else "Normal"
49
 
50
- return category, urgent
51
-
52
- # 6. Interfaz de usuario con Gradio
53
- with gr.Blocks(title="Clasificador de Tickets") as demo:
54
- gr.Markdown("## 🚀 Clasificador Automático de Tickets")
55
 
56
- with gr.Row():
57
- input_text = gr.Textbox(
58
- label="Descripción del ticket",
59
- placeholder="Escribe el problema aquí...",
60
- lines=3
61
- )
62
 
63
- with gr.Row():
64
- classify_btn = gr.Button("Clasificar", variant="primary")
 
 
 
 
65
 
66
  with gr.Row():
67
- category_output = gr.Label(label="Categoría")
68
- urgency_output = gr.Label(label="Urgencia")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
- gr.Examples(
71
- examples=[
72
- ["Mi paquete no llegó ayer, ¡es urgente!", "logística"],
73
- ["Error 500 al procesar mi tarjeta de crédito", "pagos"],
74
- ["El producto llegó con la pantalla rota", "producto defectuoso"],
75
- ["No puedo acceder a mi cuenta premium", "cuenta"],
76
- ["Factura con impuestos incorrectos", "facturación"]
77
- ],
78
  inputs=input_text,
79
- outputs=[category_output, urgency_output],
80
- fn=classify_ticket
81
  )
82
 
83
- classify_btn.click(
84
- fn=classify_ticket,
85
- inputs=input_text,
86
- outputs=[category_output, urgency_output]
87
  )
88
 
89
- # 7. Ejecutar la aplicación
90
  if __name__ == "__main__":
91
- demo.launch(server_name="0.0.0.0", server_port=7860)
92
-
 
 
 
 
1
+ import os
2
+ import re
3
+ import time
4
+ import json
5
+ import random
6
+ import requests
7
+ import gradio as gr
8
  import pandas as pd
9
  import torch
10
+ from dotenv import load_dotenv
11
  from transformers import pipeline, AutoTokenizer
 
 
12
 
13
+ # Cargar variables de entorno
14
+ load_dotenv()
15
+
16
+ # 1. Configuración del modelo
17
+ MODEL_NAME = "MoritzLaurer/mDeBERTa-v3-base-mnli-xnli"
18
  CATEGORIES = ["logística", "pagos", "producto defectuoso", "cuenta", "facturación", "otros"]
19
  URGENCY_PATTERNS = [
20
  r"\b(urgente|inmediato|cr[íi]tico|asap)\b",
 
23
  r"\b(prioridad [1-3]|nivel [1-3])\b"
24
  ]
25
 
26
+ # 2. Clase para manejo de tickets
27
+ class TicketSystem:
28
+ def __init__(self):
29
+ self.mode = os.getenv("TICKET_API_MODE", "simulated")
30
+ self.tickets = []
31
+ self.next_id = 1000
32
+
33
+ def create_ticket(self, description: str, category: str, urgent: bool):
34
+ """Crea un ticket en Zendesk o modo simulado"""
35
+ if self.mode == "zendesk":
36
+ return self._create_zendesk_ticket(description, category, urgent)
37
+ else:
38
+ return self._create_simulated_ticket(description, category, urgent)
39
+
40
+ def _create_simulated_ticket(self, description: str, category: str, urgent: bool):
41
+ ticket = {
42
+ "id": self.next_id,
43
+ "description": description,
44
+ "category": category,
45
+ "urgent": urgent,
46
+ "status": "open",
47
+ "assigned_to": "Agente Humano" if urgent else "Sistema Automático",
48
+ "created_at": time.strftime("%Y-%m-%d %H:%M:%S"),
49
+ "source": "Simulado"
50
+ }
51
+ self.tickets.append(ticket)
52
+ self.next_id += 1
53
+ self.save_to_json()
54
+ return ticket
55
+
56
+ def _create_zendesk_ticket(self, description: str, category: str, urgent: bool):
57
+ """Crea un ticket real en Zendesk"""
58
+ subdomain = os.getenv("ZENDESK_SUBDOMAIN")
59
+ email = os.getenv("ZENDESK_EMAIL")
60
+ api_token = os.getenv("ZENDESK_API_TOKEN")
61
+
62
+ priority = "urgent" if urgent else "normal"
63
+ subject = f"[{category}] {'[URGENTE] ' if urgent else ''}Ticket Automático"
64
+
65
+ data = {
66
+ "ticket": {
67
+ "subject": subject,
68
+ "comment": {"body": description},
69
+ "priority": priority,
70
+ "tags": ["auto_classified", category],
71
+ "type": "problem"
72
+ }
73
+ }
74
+
75
+ try:
76
+ response = requests.post(
77
+ f"https://{subdomain}.zendesk.com/api/v2/tickets.json",
78
+ json=data,
79
+ auth=(f"{email}/token", api_token),
80
+ headers={"Content-Type": "application/json"}
81
+ )
82
+
83
+ if response.status_code == 201:
84
+ ticket_data = response.json().get("ticket", {})
85
+ ticket = {
86
+ "id": ticket_data["id"],
87
+ "description": description,
88
+ "category": category,
89
+ "urgent": urgent,
90
+ "status": ticket_data.get("status", "open"),
91
+ "assigned_to": "Agente Humano" if urgent else "Sistema Automático",
92
+ "created_at": ticket_data.get("created_at", time.strftime("%Y-%m-%d %H:%M:%S")),
93
+ "source": "Zendesk"
94
+ }
95
+ self.tickets.append(ticket)
96
+ self.save_to_json()
97
+ return ticket
98
+ else:
99
+ error_msg = f"Error {response.status_code}: {response.text}"
100
+ return {"error": error_msg}
101
+
102
+ except Exception as e:
103
+ return {"error": str(e)}
104
+
105
+ def get_tickets(self):
106
+ return self.tickets
107
+
108
+ def save_to_json(self, filename="tickets_db.json"):
109
+ with open(filename, 'w') as f:
110
+ json.dump(self.tickets, f, indent=2)
111
+
112
+ # 3. Cargar modelo de clasificación
113
+ try:
114
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
115
+ classifier = pipeline(
116
+ "zero-shot-classification",
117
+ model=MODEL_NAME,
118
+ tokenizer=tokenizer,
119
+ device=0 if torch.cuda.is_available() else -1,
120
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32
121
+ )
122
+ MODEL_LOADED = True
123
+ except Exception as e:
124
+ print(f"Error cargando el modelo: {e}")
125
+ MODEL_LOADED = False
126
 
127
+ # 4. Funciones de clasificación
128
  def is_urgent(text: str) -> bool:
129
  text = text.lower()
130
  if any(re.search(pattern, text, flags=re.IGNORECASE) for pattern in URGENCY_PATTERNS):
131
  return True
132
+ return False
 
 
 
133
 
 
134
  def classify_text(text: str) -> str:
135
+ if not MODEL_LOADED:
136
+ return random.choice(CATEGORIES)
137
  result = classifier(text, CATEGORIES, multi_label=False)
138
  return result['labels'][0]
139
 
140
+ # 5. Inicializar sistema de tickets
141
+ ticket_system = TicketSystem()
142
+
143
+ # 6. Función para procesar tickets
144
+ def process_ticket(text):
145
  if not text.strip():
146
+ return "", "", ""
147
 
148
  category = classify_text(text)
149
+ urgent = is_urgent(text)
150
 
151
+ # Crear ticket en el sistema
152
+ ticket = ticket_system.create_ticket(text, category, urgent)
 
 
 
153
 
154
+ # Manejar errores
155
+ if isinstance(ticket, dict) and "error" in ticket:
156
+ status = f"🔴 ERROR: {ticket['error']}"
157
+ else:
158
+ status = "🔴 URGENTE - Asignado a Agente Humano" if urgent else "🟢 Enviado a Sistema Automático"
 
159
 
160
+ return category, "SÍ" if urgent else "NO", status
161
+
162
+ # 7. Interfaz de usuario con Gradio
163
+ with gr.Blocks(title="Sistema de Soporte Inteligente", theme=gr.themes.Soft()) as demo:
164
+ gr.Markdown("# 🚀 Sistema Clasificador de Tickets")
165
+ gr.Markdown(f"**Modo actual:** `{ticket_system.mode.upper()}` | [Documentación API](https://developer.zendesk.com/)")
166
 
167
  with gr.Row():
168
+ with gr.Column():
169
+ input_text = gr.Textbox(
170
+ label="Descripción del problema",
171
+ placeholder="Escribe aquí el problema del cliente...",
172
+ lines=4
173
+ )
174
+ submit_btn = gr.Button("Procesar Ticket", variant="primary")
175
+
176
+ with gr.Accordion("Ejemplos Rápidos", open=False):
177
+ gr.Examples(
178
+ examples=[
179
+ ["Mi paquete no llegó a tiempo, ¡es urgente!"],
180
+ ["Error 500 al procesar mi tarjeta de crédito"],
181
+ ["El producto llegó con la pantalla rota"],
182
+ ["No puedo acceder a mi cuenta premium"],
183
+ ["Factura con impuestos incorrectos"]
184
+ ],
185
+ inputs=[input_text]
186
+ )
187
+
188
+ with gr.Column():
189
+ category_out = gr.Textbox(label="Categoría")
190
+ urgency_out = gr.Textbox(label="¿Urgente?")
191
+ status_out = gr.Textbox(label="Estado del Ticket")
192
+
193
+ with gr.Accordion("Base de Tickets", open=False):
194
+ ticket_db = gr.JSON(label="Tickets Registrados")
195
+ update_btn = gr.Button("Actualizar Base de Datos")
196
+
197
+ with gr.Accordion("Configuración API", open=False):
198
+ mode_info = gr.Markdown(f"Modo actual: **{ticket_system.mode}**")
199
+ api_status = gr.Markdown("Credenciales Zendesk: " +
200
+ ("✅ Configuradas" if os.getenv("ZENDESK_SUBDOMAIN") else "❌ No configuradas"))
201
 
202
+ # Event handlers
203
+ submit_btn.click(
204
+ fn=process_ticket,
 
 
 
 
 
205
  inputs=input_text,
206
+ outputs=[category_out, urgency_out, status_out]
 
207
  )
208
 
209
+ update_btn.click(
210
+ fn=lambda: ticket_system.get_tickets(),
211
+ inputs=[],
212
+ outputs=ticket_db
213
  )
214
 
215
+ # 8. Ejecutar la aplicación
216
  if __name__ == "__main__":
217
+ demo.launch(
218
+ server_name="0.0.0.0",
219
+ server_port=7860,
220
+ show_error=True
221
+ )