@innvisor/conny-ai 9.7.0 → 9.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +17 -1
  3. package/conny_app.py +9 -3
  4. package/conny_cli.py +103 -11
  5. package/conny_core/evolution.py +112 -0
  6. package/conny_core/first_turn_ops.py +16 -20
  7. package/conny_core/prompt_ops.py +62 -0
  8. package/conny_demo_voice.py +1 -1
  9. package/conny_doctor.py +287 -2
  10. package/conny_domino.py +2 -2
  11. package/conny_generator.py +1 -1
  12. package/conny_i18n.py +81 -2
  13. package/conny_init.py +254 -41
  14. package/conny_runtime_ops.py +198 -6
  15. package/conny_tui.py +7 -0
  16. package/conny_ultra_config.py +25 -11
  17. package/conny_utils.py +21 -3
  18. package/ecosystem.config.js +11 -1
  19. package/install.sh +78 -22
  20. package/npm/conny.js +75 -21
  21. package/package.json +12 -2
  22. package/run.sh +7 -0
  23. package/src/conny/admin/dashboard.py +35 -4
  24. package/src/conny/admin_memory.py +93 -0
  25. package/src/conny/api/routes.py +26 -9
  26. package/src/conny/channels/cli.py +30 -9
  27. package/src/conny/demo/handler.py +23 -23
  28. package/src/conny/personas/generator.py +1 -1
  29. package/src/conny/production/domino.py +2 -2
  30. package/src/conny/production/guard.py +4 -4
  31. package/src/core/admin_engines.py +51 -48
  32. package/src/core/globals.py +110 -9
  33. package/src/core/production_monitor.py +63 -38
  34. package/src/core/runtime.py +343 -305
  35. package/src/domain/prompts/prospect_pitch.py +11 -11
  36. package/src/domain/send_guard.py +4 -4
  37. package/src/interfaces/web/app.py +91 -27
  38. package/src/interfaces/web/demo_admin_commands.py +165 -0
  39. package/src/interfaces/web/demo_handler.py +178 -34
  40. package/brand-assets/cl-nica-de-las-am-ricas/manifest.json +0 -22
  41. package/brand-assets/cl-nica-de-las-am-ricas/processed/business-identity.txt +0 -11
  42. package/brand-assets/cl-nica-de-las-am-ricas/raw/business-identity.txt +0 -11
  43. package/brand-assets/cl-nica-las-am-ricas/manifest.json +0 -22
  44. package/brand-assets/cl-nica-las-am-ricas/processed/business-identity.txt +0 -11
  45. package/brand-assets/cl-nica-las-am-ricas/raw/business-identity.txt +0 -11
  46. package/brand-assets/conny-demo/manifest.json +0 -22
  47. package/brand-assets/conny-demo/processed/business-identity.txt +0 -7
  48. package/brand-assets/conny-demo/raw/business-identity.txt +0 -7
  49. package/fix_init.py +0 -27
  50. package/verify_conversation_impl.py +0 -48
@@ -127,11 +127,10 @@ class ConnyProduction:
127
127
  # Load soul knowledge
128
128
  soul_context = ""
129
129
  try:
130
- soul_file = Path(f"soul/{instance_id}/knowledge.md")
131
- if soul_file.exists():
132
- soul_context = soul_file.read_text()[-2000:]
133
- except Exception:
134
- pass
130
+ from conny_core.prompt_ops import build_business_context
131
+ soul_context = build_business_context(clinic, db, instance_id)
132
+ except Exception as e:
133
+ log.warning(f"[production] error building business context: {e}")
135
134
 
136
135
  tone_instructions = {
137
136
  "luxury": "Tono LUXURY: sofisticada, elegante, exclusiva. Usa lenguaje premium. Nunca suenes informal ni uses jerga. Transmite exclusividad en cada palabra.",
@@ -142,39 +141,55 @@ class ConnyProduction:
142
141
  }
143
142
  tone_instruction = tone_instructions.get(persona_tone, tone_instructions["colombian_warm"])
144
143
 
145
- sys_prompt = f"""Eres Conny, recepcionista virtual de {clinic_name}.
146
- Servicios: {services_str}
147
- Horario: {schedule or 'consultar'}
148
-
149
- TONO: {tone_instruction}
150
-
151
- {f"CONOCIMIENTO DEL NEGOCIO:{chr(10)}{soul_context}" if soul_context else ""}
152
-
153
- {f"SOBRE ESTE PACIENTE:{chr(10)}{patient_context}" if patient_context else ""}
154
- {f"{chr(10)}{lang_instruction}" if lang_instruction else ""}
155
-
156
- REGLA #1 — RESPONDE CON LO QUE SABES:
157
- - Si la respuesta está en la sección "RESPUESTAS QUE YA SABES" → DALA DIRECTAMENTE sin dudar
158
- - Si NO encuentras la respuesta en ninguna sección → "me confirmo y te aviso"
159
- - Las RESPUESTAS QUE YA SABES son información VERIFICADA por el dueño. Úsalas con total confianza.
160
-
161
- REGLAS GENERALES:
162
- - {tone_instruction.split(':')[0]} — aplica este tono en CADA respuesta
163
- - Una sola pregunta por turno, enfocada en avanzar la conversación
164
- - Si el paciente quiere cita: pide nombre, servicio, fecha preferida
165
- - NUNCA digas "no tengo capacidad", "está fuera de mi alcance"
166
- - Si preguntan "eres IA?" responde HONESTA y breve: "sí, soy una IA 😊 pero estoy aquí pa ayudarte, dime en qué te puedo servir"
167
- - NUNCA evadas la pregunta de si eres IA. Sé directa, no insistas ni te pongas a la defensiva
168
- - NUNCA uses formato markdown (**, *, _, #, `)
169
- - Usa máximo 2-3 burbujas separadas por |||
170
- - Sé concisa (máx 40 palabras por burbuja)
171
- - Escribe EXACTAMENTE como una persona de 28 años en WhatsApp: mensajes cortos, naturales
172
- - Emojis: usa MÁXIMO 1 por conversación, y solo en el saludo inicial. Después de eso, 0 emojis. Nada de 😊 genérico en cada mensaje.
173
- - Si ya saludaste, no vuelvas a saludar
174
- - NUNCA te presentes con "Soy Conny tu recepcionista de X" — eso suena a bot
175
- - Si es el primer mensaje, saluda así: "{time_greeting}! hablas con Conny 😊 ||| en qué te puedo ayudar?"
176
- - Separa SIEMPRE en 2-3 burbujas cortas (|||), nunca un solo bloque largo
177
- - NUNCA digas el nombre completo de la clínica en el saludo — el paciente ya sabe dónde escribió"""
144
+ # Admin rules injection (things admin said to ask first or forbidden phrases)
145
+ forbidden_txt = ""
146
+ try:
147
+ rules = clinic.get("business_rules", {})
148
+ if isinstance(rules, str):
149
+ import json as _j4
150
+ rules = _j4.loads(rules)
151
+
152
+ forbidden = rules.get("forbidden_phrases", [])
153
+ if forbidden:
154
+ forbidden_txt = "\nREGLAS CRÍTICAS (No usar NUNCA estas frases/palabras):\n" + "\n".join([f"- {p}" for p in forbidden])
155
+ except Exception:
156
+ pass
157
+
158
+ # PROMPT DE PRODUCCIÓN UPGRADED
159
+ sys_prompt = f"""
160
+ Eres Conny, la recepcionista virtual de {clinic_name}. Tu identidad es Innvisor.
161
+
162
+ IDENTIDAD
163
+ No eres un bot. Eres Conny la persona que atiende este WhatsApp.
164
+ Nunca digas "soy una IA", "soy un asistente virtual" ni "soy un bot" a menos que te lo pregunten directamente.
165
+ Si te preguntan directamente si eres IA, confirmas con naturalidad y sigues adelante.
166
+
167
+ LO QUE SABES DE ESTE NEGOCIO (LEÍDO DE TUS CARPETAS/ARCHIVOS)
168
+ {soul_context}
169
+ {forbidden_txt}
170
+
171
+ LO QUE SABES DE ESTE CLIENTE
172
+ {patient_context}
173
+
174
+ CÓMO HABLAS
175
+ - Responde de manera sumamente humana, natural y empática. Usa buena ortografía y puntuación correcta, pero mantén un tono conversacional (no robótico ni extremadamente formal).
176
+ - Eres cálida y profesional. Transmites confianza.
177
+ - Mensajes cortos. Máximo 2-3 ideas por respuesta. Separa las ideas con ||| (para enviar múltiples burbujas).
178
+ - Sin frases enlatadas o de call center: nada de "con mucho gusto", "en qué le puedo ayudar", "fue un placer". Sé original y auténtica en cada respuesta.
179
+ - TONO: {tone_instruction}
180
+
181
+ LO QUE HACES Y NO HACES
182
+ - Responder preguntas sobre {clinic_name} usando ÚNICAMENTE la información que tienes en "LO QUE SABES DE ESTE NEGOCIO".
183
+ - NUNCA inventes precios, servicios, horarios ni detalles que no conoces con certeza. Si no lo sabes, NO asumas.
184
+ - Si no tienes la información exacta que pide el cliente, detente y dile amablemente: "Permíteme un segundo, voy a validar este detalle exacto con el administrador para darte la información correcta."
185
+ - Si el cliente insiste y sigues sin saberlo, pide disculpas y reitera que estás esperando la respuesta del administrador.
186
+ - Orientar hacia una cita o venta solo cuando estás segura de los datos y el cliente muestra intención.
187
+
188
+ ESCALACIÓN
189
+ Si un cliente pregunta algo que no puedes responder:
190
+ 1. Dile al cliente que vas a consultar o confirmar.
191
+ 2. Anotas mentalmente que el dueño debe enseñarte eso para que cuando hables con él (el admin), lo presiones para que te enseñe a responder eso.
192
+ """
178
193
 
179
194
  messages = [{"role": "system", "content": sys_prompt}]
180
195
  for m in history[-12:]:
@@ -308,6 +323,16 @@ REGLAS GENERALES:
308
323
  try:
309
324
  await self.conny._send_message(admin_jid, alert_msg)
310
325
  log.info(f"[production] admin alerted: confidence={confidence:.2f} question='{text[:50]}'")
326
+
327
+ # Guardar como pregunta pendiente para el admin
328
+ if not hasattr(self.conny, "_admin_pending"):
329
+ self.conny._admin_pending = {}
330
+ self.conny._admin_pending[admin_jid] = {
331
+ "action": "answer_gap",
332
+ "patient_chat_id": chat_id,
333
+ "original_question": text,
334
+ "ts": time.time()
335
+ }
311
336
  except Exception as e:
312
337
  log.warning(f"[production] failed to alert admin: {e}")
313
338