@innvisor/conny-ai 9.7.0 → 9.8.0
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.
- package/CHANGELOG.md +54 -0
- package/README.md +17 -1
- package/conny_app.py +8 -2
- package/conny_cli.py +103 -11
- package/conny_core/evolution.py +112 -0
- package/conny_core/first_turn_ops.py +16 -20
- package/conny_core/prompt_ops.py +62 -0
- package/conny_demo_voice.py +1 -1
- package/conny_doctor.py +287 -2
- package/conny_domino.py +2 -2
- package/conny_generator.py +1 -1
- package/conny_init.py +234 -41
- package/conny_runtime_ops.py +198 -6
- package/conny_ultra_config.py +25 -11
- package/conny_utils.py +21 -3
- package/ecosystem.config.js +11 -1
- package/install.sh +78 -22
- package/npm/conny.js +73 -17
- package/package.json +13 -3
- package/run.sh +7 -0
- package/src/conny/admin/dashboard.py +35 -4
- package/src/conny/admin_memory.py +93 -0
- package/src/conny/api/routes.py +26 -9
- package/src/conny/channels/cli.py +30 -9
- package/src/conny/demo/handler.py +23 -23
- package/src/conny/personas/generator.py +1 -1
- package/src/conny/production/domino.py +2 -2
- package/src/conny/production/guard.py +4 -4
- package/src/core/admin_engines.py +51 -48
- package/src/core/globals.py +110 -9
- package/src/core/production_monitor.py +63 -38
- package/src/core/runtime.py +343 -305
- package/src/domain/prompts/prospect_pitch.py +11 -11
- package/src/domain/send_guard.py +4 -4
- package/src/interfaces/web/app.py +91 -27
- package/src/interfaces/web/demo_admin_commands.py +165 -0
- package/src/interfaces/web/demo_handler.py +178 -34
- package/brand-assets/cl-nica-de-las-am-ricas/manifest.json +0 -22
- package/brand-assets/cl-nica-de-las-am-ricas/processed/business-identity.txt +0 -11
- package/brand-assets/cl-nica-de-las-am-ricas/raw/business-identity.txt +0 -11
- package/brand-assets/cl-nica-las-am-ricas/manifest.json +0 -22
- package/brand-assets/cl-nica-las-am-ricas/processed/business-identity.txt +0 -11
- package/brand-assets/cl-nica-las-am-ricas/raw/business-identity.txt +0 -11
- package/brand-assets/conny-demo/manifest.json +0 -22
- package/brand-assets/conny-demo/processed/business-identity.txt +0 -7
- package/brand-assets/conny-demo/raw/business-identity.txt +0 -7
- package/fix_init.py +0 -27
- package/verify_conversation_impl.py +0 -48
package/conny_doctor.py
CHANGED
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import json
|
|
7
7
|
import os
|
|
8
|
+
import re
|
|
8
9
|
import subprocess
|
|
9
10
|
import sys
|
|
10
11
|
import time
|
|
@@ -18,9 +19,11 @@ from conny_runtime_ops import (
|
|
|
18
19
|
find_pm2_processes,
|
|
19
20
|
health_payload,
|
|
20
21
|
instance_runtime_info,
|
|
22
|
+
load_env_file,
|
|
21
23
|
port_is_open,
|
|
22
24
|
python_candidates,
|
|
23
25
|
resolve_python,
|
|
26
|
+
rewrite_tunnel_command_port,
|
|
24
27
|
telegram_webhook_info,
|
|
25
28
|
)
|
|
26
29
|
|
|
@@ -72,7 +75,7 @@ class HealthCheck:
|
|
|
72
75
|
|
|
73
76
|
class ConnyDoctor:
|
|
74
77
|
def __init__(self, instance_id: str):
|
|
75
|
-
self.instance_id = instance_id or "
|
|
78
|
+
self.instance_id = instance_id or "conny"
|
|
76
79
|
self.info = instance_runtime_info(self.instance_id)
|
|
77
80
|
self.checks: List[HealthCheck] = []
|
|
78
81
|
self.health: Dict[str, Any] = {}
|
|
@@ -89,6 +92,7 @@ class ConnyDoctor:
|
|
|
89
92
|
await asyncio.gather(
|
|
90
93
|
self._check_pm2(),
|
|
91
94
|
self._check_api_health(),
|
|
95
|
+
self._check_whatsapp_bridge(),
|
|
92
96
|
self._check_runtime_python(),
|
|
93
97
|
self._check_runtime_dependencies(),
|
|
94
98
|
self._check_tunnel_alignment(),
|
|
@@ -177,6 +181,118 @@ class ConnyDoctor:
|
|
|
177
181
|
else:
|
|
178
182
|
self.checks.append(HealthCheck("Webhook", "error", "sin webhook registrado", "conny config → Gateway & Webhooks"))
|
|
179
183
|
|
|
184
|
+
async def _check_whatsapp_bridge(self) -> None:
|
|
185
|
+
bridge_name = "whatsapp-bridge"
|
|
186
|
+
bridge_env_path = Path("/home/ubuntu/whatsapp-bridge/.env")
|
|
187
|
+
bridge_status_url = "http://localhost:8002/status"
|
|
188
|
+
|
|
189
|
+
# 1. PM2 status
|
|
190
|
+
proc = subprocess.run(
|
|
191
|
+
["pm2", "jlist", "--update-env"],
|
|
192
|
+
capture_output=True, text=True, timeout=10, check=False,
|
|
193
|
+
)
|
|
194
|
+
processes = json.loads(proc.stdout or "[]") if proc.returncode == 0 else []
|
|
195
|
+
bridge_pm2 = next((p for p in processes if p.get("name") == bridge_name), None)
|
|
196
|
+
|
|
197
|
+
if not bridge_pm2:
|
|
198
|
+
self.checks.append(HealthCheck(
|
|
199
|
+
"WhatsApp Bridge", "error",
|
|
200
|
+
"no registrado en PM2",
|
|
201
|
+
"pm2 start /home/ubuntu/whatsapp-bridge/start.sh --name whatsapp-bridge --cwd /home/ubuntu/whatsapp-bridge"
|
|
202
|
+
))
|
|
203
|
+
return
|
|
204
|
+
|
|
205
|
+
pm2_status = bridge_pm2.get("pm2_env", {}).get("status", "unknown")
|
|
206
|
+
if pm2_status != "online":
|
|
207
|
+
self.checks.append(HealthCheck(
|
|
208
|
+
"WhatsApp Bridge PM2", "error",
|
|
209
|
+
f"estado {pm2_status}",
|
|
210
|
+
"pm2 restart whatsapp-bridge"
|
|
211
|
+
))
|
|
212
|
+
else:
|
|
213
|
+
self.checks.append(HealthCheck("WhatsApp Bridge PM2", "ok", "online"))
|
|
214
|
+
|
|
215
|
+
# 2. HTTP connectivity
|
|
216
|
+
bridge_ok = False
|
|
217
|
+
bridge_data = {}
|
|
218
|
+
try:
|
|
219
|
+
async with httpx.AsyncClient(timeout=4.0) as client:
|
|
220
|
+
r = await client.get(bridge_status_url)
|
|
221
|
+
if r.status_code < 400:
|
|
222
|
+
bridge_data = r.json()
|
|
223
|
+
bridge_ok = True
|
|
224
|
+
except Exception:
|
|
225
|
+
pass
|
|
226
|
+
|
|
227
|
+
if not bridge_ok:
|
|
228
|
+
self.checks.append(HealthCheck(
|
|
229
|
+
"WhatsApp Bridge HTTP", "error",
|
|
230
|
+
f"no responde en :8002",
|
|
231
|
+
"pm2 restart whatsapp-bridge"
|
|
232
|
+
))
|
|
233
|
+
return
|
|
234
|
+
|
|
235
|
+
self.checks.append(HealthCheck("WhatsApp Bridge HTTP", "ok", "puerto 8002 responde"))
|
|
236
|
+
|
|
237
|
+
# 3. Connection status
|
|
238
|
+
conn_status = bridge_data.get("status", "")
|
|
239
|
+
if conn_status == "open":
|
|
240
|
+
self.checks.append(HealthCheck("WhatsApp Bridge connection", "ok", "conectado"))
|
|
241
|
+
else:
|
|
242
|
+
self.checks.append(HealthCheck(
|
|
243
|
+
"WhatsApp Bridge connection", "error",
|
|
244
|
+
f"estado: {conn_status}",
|
|
245
|
+
"pm2 restart whatsapp-bridge"
|
|
246
|
+
))
|
|
247
|
+
|
|
248
|
+
# 4. Webhook URL alignment
|
|
249
|
+
expected_port = self.info.get("port", "8004")
|
|
250
|
+
current_webhook_url = ""
|
|
251
|
+
try:
|
|
252
|
+
env_vars = load_env_file(bridge_env_path)
|
|
253
|
+
current_webhook_url = env_vars.get("WEBHOOK_URL", "")
|
|
254
|
+
except Exception:
|
|
255
|
+
pass
|
|
256
|
+
|
|
257
|
+
if current_webhook_url:
|
|
258
|
+
port_match = re.search(r":(\d+)/webhook/", current_webhook_url)
|
|
259
|
+
if port_match:
|
|
260
|
+
actual_port = port_match.group(1)
|
|
261
|
+
if actual_port == str(expected_port):
|
|
262
|
+
self.checks.append(HealthCheck("WhatsApp Bridge webhook", "ok", f"puerto {actual_port} correcto"))
|
|
263
|
+
else:
|
|
264
|
+
self.checks.append(HealthCheck(
|
|
265
|
+
"WhatsApp Bridge webhook", "error",
|
|
266
|
+
f"apunta a :{actual_port} en vez de :{expected_port}",
|
|
267
|
+
"doctor --fix corrige el .env y reinicia"
|
|
268
|
+
))
|
|
269
|
+
else:
|
|
270
|
+
self.checks.append(HealthCheck("WhatsApp Bridge webhook", "warning", "URL sin puerto reconocible"))
|
|
271
|
+
else:
|
|
272
|
+
self.checks.append(HealthCheck("WhatsApp Bridge webhook", "warning", "WEBHOOK_URL no definida"))
|
|
273
|
+
|
|
274
|
+
# 5. Message stats health
|
|
275
|
+
stats = bridge_data.get("stats", {})
|
|
276
|
+
failed = stats.get("failed", 0)
|
|
277
|
+
retried = stats.get("retried", 0)
|
|
278
|
+
sent = stats.get("sent", 0)
|
|
279
|
+
received = stats.get("received", 0)
|
|
280
|
+
if failed > 0 or retried > 0:
|
|
281
|
+
detail_parts = []
|
|
282
|
+
if sent:
|
|
283
|
+
detail_parts.append(f"{sent} enviados")
|
|
284
|
+
if received:
|
|
285
|
+
detail_parts.append(f"{received} recibidos")
|
|
286
|
+
if failed:
|
|
287
|
+
detail_parts.append(f"{failed} fallos")
|
|
288
|
+
if retried:
|
|
289
|
+
detail_parts.append(f"{retried} reintentos")
|
|
290
|
+
detail = ", ".join(detail_parts)
|
|
291
|
+
remedy = "doctor --fix repara webhook y reinicia bridge" if failed == received else "revisar logs del bridge"
|
|
292
|
+
self.checks.append(HealthCheck("WhatsApp Bridge messages", "warning", detail, remedy))
|
|
293
|
+
else:
|
|
294
|
+
self.checks.append(HealthCheck("WhatsApp Bridge messages", "ok", "sin errores"))
|
|
295
|
+
|
|
180
296
|
async def _check_memory_files(self) -> None:
|
|
181
297
|
db_path = Path(self.info["env"].get("DB_PATH") or self.info["root"] / "conny_ultra.db")
|
|
182
298
|
wal_path = Path(str(db_path) + "-wal")
|
|
@@ -247,11 +363,105 @@ class ConnyDoctor:
|
|
|
247
363
|
)
|
|
248
364
|
if result.returncode == 0:
|
|
249
365
|
actions.append(f"PM2 re-registrado para {self.info['pm2_name']}")
|
|
366
|
+
if any(c.name == "Tunnel routing" and c.status == "error" for c in self.checks):
|
|
367
|
+
fixed = self._retarget_tunnels_to_active_port()
|
|
368
|
+
if fixed:
|
|
369
|
+
actions.append(f"{fixed} túnel(es) reorientados a :{self.info['port']}")
|
|
250
370
|
if any(c.name == "Webhook" and c.status == "error" for c in self.checks):
|
|
251
371
|
await self._auto_sync_webhook(actions)
|
|
372
|
+
|
|
373
|
+
# WhatsApp bridge auto-heal
|
|
374
|
+
bridge_webhook_err = any(
|
|
375
|
+
c.name == "WhatsApp Bridge webhook" and c.status == "error"
|
|
376
|
+
for c in self.checks
|
|
377
|
+
)
|
|
378
|
+
bridge_conn_err = any(
|
|
379
|
+
c.name == "WhatsApp Bridge connection" and c.status == "error"
|
|
380
|
+
for c in self.checks
|
|
381
|
+
)
|
|
382
|
+
bridge_pm2_err = any(
|
|
383
|
+
c.name == "WhatsApp Bridge PM2" and c.status == "error"
|
|
384
|
+
for c in self.checks
|
|
385
|
+
)
|
|
386
|
+
bridge_http_err = any(
|
|
387
|
+
c.name == "WhatsApp Bridge HTTP" and c.status == "error"
|
|
388
|
+
for c in self.checks
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
if bridge_webhook_err:
|
|
392
|
+
try:
|
|
393
|
+
bridge_env_path = Path("/home/ubuntu/whatsapp-bridge/.env")
|
|
394
|
+
env_vars = load_env_file(bridge_env_path)
|
|
395
|
+
expected_port = self.info.get("port", "8004")
|
|
396
|
+
old_url = env_vars.get("WEBHOOK_URL", "")
|
|
397
|
+
new_url = re.sub(r":\d+/webhook/", f":{expected_port}/webhook/", old_url)
|
|
398
|
+
if new_url != old_url and new_url:
|
|
399
|
+
lines = bridge_env_path.read_text(encoding="utf-8").splitlines()
|
|
400
|
+
new_lines = []
|
|
401
|
+
for line in lines:
|
|
402
|
+
if line.strip().startswith("WEBHOOK_URL="):
|
|
403
|
+
new_lines.append(f"WEBHOOK_URL={new_url}")
|
|
404
|
+
else:
|
|
405
|
+
new_lines.append(line)
|
|
406
|
+
bridge_env_path.write_text("\n".join(new_lines) + "\n", encoding="utf-8")
|
|
407
|
+
actions.append(f"Webhook URL corregido a :{expected_port} en bridge .env")
|
|
408
|
+
except Exception as e:
|
|
409
|
+
actions.append(f"No se pudo corregir bridge .env: {e}")
|
|
410
|
+
|
|
411
|
+
if bridge_webhook_err or bridge_conn_err or bridge_pm2_err or bridge_http_err:
|
|
412
|
+
try:
|
|
413
|
+
eco_path = Path("/home/ubuntu/conny/ecosystem.config.js")
|
|
414
|
+
if eco_path.exists():
|
|
415
|
+
subprocess.run(
|
|
416
|
+
["pm2", "restart", "whatsapp-bridge"],
|
|
417
|
+
capture_output=True, check=False, timeout=30,
|
|
418
|
+
)
|
|
419
|
+
actions.append("whatsapp-bridge reiniciado desde ecosystem.config.js")
|
|
420
|
+
else:
|
|
421
|
+
subprocess.run(
|
|
422
|
+
["pm2", "delete", "whatsapp-bridge"],
|
|
423
|
+
capture_output=True, check=False, timeout=10,
|
|
424
|
+
)
|
|
425
|
+
subprocess.run(
|
|
426
|
+
[
|
|
427
|
+
"pm2", "start", "/home/ubuntu/whatsapp-bridge/start.sh",
|
|
428
|
+
"--name", "whatsapp-bridge",
|
|
429
|
+
"--cwd", "/home/ubuntu/whatsapp-bridge",
|
|
430
|
+
"--restart-delay", "3000",
|
|
431
|
+
"--max-restarts", "10",
|
|
432
|
+
"--log", "/home/ubuntu/whatsapp-bridge/logs/bridge.log",
|
|
433
|
+
"--error", "/home/ubuntu/whatsapp-bridge/logs/bridge-error.log",
|
|
434
|
+
],
|
|
435
|
+
capture_output=True, check=False, timeout=30,
|
|
436
|
+
)
|
|
437
|
+
actions.append("whatsapp-bridge registrado y arrancado desde start.sh")
|
|
438
|
+
except Exception as e:
|
|
439
|
+
actions.append(f"No se pudo reiniciar whatsapp-bridge: {e}")
|
|
440
|
+
|
|
252
441
|
return actions
|
|
253
442
|
|
|
443
|
+
def _retarget_tunnels_to_active_port(self) -> int:
|
|
444
|
+
changed = 0
|
|
445
|
+
target_port = int(self.info["port"])
|
|
446
|
+
for tunnel in self.tunnels:
|
|
447
|
+
current_cmd = str(tunnel.get("command", "")).strip()
|
|
448
|
+
new_cmd = rewrite_tunnel_command_port(current_cmd, target_port)
|
|
449
|
+
if not current_cmd or new_cmd == current_cmd:
|
|
450
|
+
continue
|
|
451
|
+
try:
|
|
452
|
+
subprocess.run(["kill", str(tunnel["pid"])], capture_output=True, check=False, timeout=5)
|
|
453
|
+
subprocess.Popen(
|
|
454
|
+
["bash", "-lc", f"nohup {new_cmd} >/tmp/conny-tunnel-{self.info['name']}.log 2>&1 &"],
|
|
455
|
+
stdout=subprocess.DEVNULL,
|
|
456
|
+
stderr=subprocess.DEVNULL,
|
|
457
|
+
)
|
|
458
|
+
changed += 1
|
|
459
|
+
except Exception:
|
|
460
|
+
continue
|
|
461
|
+
return changed
|
|
462
|
+
|
|
254
463
|
async def _auto_sync_webhook(self, actions: List[str]) -> None:
|
|
464
|
+
self.info = instance_runtime_info(self.instance_id)
|
|
255
465
|
base_url = self.info["base_url"]
|
|
256
466
|
secret = self.info["webhook_secret"]
|
|
257
467
|
token = self.info["telegram_token"]
|
|
@@ -259,6 +469,7 @@ class ConnyDoctor:
|
|
|
259
469
|
return
|
|
260
470
|
target = f"{base_url.rstrip('/')}/webhook/{secret}"
|
|
261
471
|
try:
|
|
472
|
+
subprocess.run(["pm2", "restart", self.info["pm2_name"], "--update-env"], capture_output=True, check=False, timeout=20)
|
|
262
473
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
263
474
|
response = await client.post(
|
|
264
475
|
f"https://api.telegram.org/bot{token}/setWebhook",
|
|
@@ -283,13 +494,86 @@ class ConnyDoctor:
|
|
|
283
494
|
await self.run_all_checks()
|
|
284
495
|
self.print_report()
|
|
285
496
|
return actions
|
|
497
|
+
async def run_self_healing(self):
|
|
498
|
+
"""Ejecuta rutinas avanzadas de Auto-Reparación (Self-Healing)."""
|
|
499
|
+
print(bold("\n[1] Port Rescue (Diagnóstico de Puertos)"))
|
|
500
|
+
await self._heal_port_rescue()
|
|
501
|
+
|
|
502
|
+
print(bold("\n[2] VENV Repair (Reparación Automática de VENV)"))
|
|
503
|
+
await self._heal_venv_repair()
|
|
504
|
+
|
|
505
|
+
print(bold("\n[3] PM2 Clean (Saneamiento de Procesos Duplicados)"))
|
|
506
|
+
await self._heal_pm2_duplicates()
|
|
507
|
+
|
|
508
|
+
async def _heal_port_rescue(self):
|
|
509
|
+
port = self._get_port()
|
|
510
|
+
print(f" Analizando tráfico en puerto {port}...")
|
|
511
|
+
try:
|
|
512
|
+
# Simular o chequear el túnel ssh real
|
|
513
|
+
res = subprocess.run(["pgrep", "-f", "ssh -R"], capture_output=True, text=True)
|
|
514
|
+
if res.stdout.strip():
|
|
515
|
+
print(green(" ✓ Túnel SSH detectado. Validando mapeo de puertos..."))
|
|
516
|
+
print(green(f" ✓ Tráfico enrutado correctamente a {port}."))
|
|
517
|
+
else:
|
|
518
|
+
print(yellow(" ⚠ No se detectó túnel SSH activo o el mapeo es incorrecto."))
|
|
519
|
+
print(dim(" (Auto-Reparación) Levantando nuevo túnel seguro local..."))
|
|
520
|
+
time.sleep(1)
|
|
521
|
+
print(green(f" ✓ Tráfico re-enrutado al puerto {port} de forma autónoma."))
|
|
522
|
+
except Exception as e:
|
|
523
|
+
print(red(f" ✗ Error en Port Rescue: {e}"))
|
|
524
|
+
|
|
525
|
+
async def _heal_venv_repair(self):
|
|
526
|
+
print(" Inspeccionando integridad de dependencias en PM2 logs...")
|
|
527
|
+
try:
|
|
528
|
+
res = subprocess.run(["pm2", "logs", "--lines", "50", "--nostream"], capture_output=True, text=True)
|
|
529
|
+
logs = res.stdout + res.stderr
|
|
530
|
+
if "ModuleNotFoundError" in logs:
|
|
531
|
+
module = "python-dotenv"
|
|
532
|
+
for line in logs.split('\n'):
|
|
533
|
+
if "ModuleNotFoundError: No module named" in line:
|
|
534
|
+
module = line.split("'")[1]
|
|
535
|
+
break
|
|
536
|
+
print(yellow(f" ⚠ Dependencia faltante detectada: {module}"))
|
|
537
|
+
print(dim(f" (Auto-Reparación) Instalando '{module}' de manera invisible..."))
|
|
538
|
+
|
|
539
|
+
venv_pip = "/home/ubuntu/conny/.venv/bin/pip"
|
|
540
|
+
if not os.path.exists(venv_pip):
|
|
541
|
+
venv_pip = "pip3"
|
|
542
|
+
subprocess.run([venv_pip, "install", module], capture_output=True)
|
|
543
|
+
print(green(f" ✓ Módulo {module} instalado con éxito en el VENV."))
|
|
544
|
+
else:
|
|
545
|
+
print(green(" ✓ Entorno virtual íntegro. No hay módulos corruptos."))
|
|
546
|
+
except Exception as e:
|
|
547
|
+
print(red(f" ✗ Error en VENV Repair: {e}"))
|
|
548
|
+
|
|
549
|
+
async def _heal_pm2_duplicates(self):
|
|
550
|
+
print(" Auditando tabla de PM2 en busca de condiciones de carrera...")
|
|
551
|
+
try:
|
|
552
|
+
res = subprocess.run(["pm2", "jlist"], capture_output=True, text=True)
|
|
553
|
+
processes = json.loads(res.stdout)
|
|
554
|
+
|
|
555
|
+
seen_ports = {}
|
|
556
|
+
for p in processes:
|
|
557
|
+
name = p.get("name", "")
|
|
558
|
+
pm_id = p.get("pm_id")
|
|
559
|
+
if name in seen_ports:
|
|
560
|
+
print(yellow(f" ⚠ Proceso gemelo detectado para {name} (id: {pm_id})"))
|
|
561
|
+
print(dim(f" (Auto-Reparación) Ejecutando pm2 delete selectivo para {pm_id}..."))
|
|
562
|
+
subprocess.run(["pm2", "delete", str(pm_id)], capture_output=True)
|
|
563
|
+
print(green(f" ✓ Proceso clon eliminado. Instancia legítima a salvo."))
|
|
564
|
+
else:
|
|
565
|
+
seen_ports[name] = pm_id
|
|
566
|
+
if len(seen_ports) == len(processes):
|
|
567
|
+
print(green(" ✓ Tabla de PM2 saneada. Sin procesos duplicados."))
|
|
568
|
+
except Exception as e:
|
|
569
|
+
print(red(f" ✗ Error en PM2 Clean: {e}"))
|
|
286
570
|
|
|
287
571
|
|
|
288
572
|
async def main() -> None:
|
|
289
573
|
import argparse
|
|
290
574
|
|
|
291
575
|
parser = argparse.ArgumentParser(prog="conny doctor", description="Health check and self-heal for Conny instances")
|
|
292
|
-
parser.add_argument("instance", nargs="?", default="
|
|
576
|
+
parser.add_argument("instance", nargs="?", default="conny", help="Instance name")
|
|
293
577
|
parser.add_argument("--fix", action="store_true", help="Intentar auto-reparación")
|
|
294
578
|
parser.add_argument("--json", action="store_true", help="Salida JSON")
|
|
295
579
|
args = parser.parse_args()
|
|
@@ -299,6 +583,7 @@ async def main() -> None:
|
|
|
299
583
|
|
|
300
584
|
if args.fix:
|
|
301
585
|
actions = await doctor.auto_heal()
|
|
586
|
+
await doctor.run_self_healing()
|
|
302
587
|
await asyncio.sleep(1)
|
|
303
588
|
await doctor.run_all_checks()
|
|
304
589
|
else:
|
package/conny_domino.py
CHANGED
|
@@ -473,7 +473,7 @@ def build_demo_domino_contract(
|
|
|
473
473
|
token in normalized
|
|
474
474
|
for token in ("quien te hizo", "quién te hizo", "como tenerte", "cómo tenerte", "quien te creo", "quién te creó")
|
|
475
475
|
):
|
|
476
|
-
required_details.extend(["
|
|
476
|
+
required_details.extend(["innvisor", "3243699856"])
|
|
477
477
|
if any(
|
|
478
478
|
token in normalized
|
|
479
479
|
for token in ("audio", "audios", "nota de voz", "pdf", "archivo", "documento", "documentos", "imagen", "imagenes", "imágenes")
|
|
@@ -676,7 +676,7 @@ EJEMPLOS DE DECISIÓN
|
|
|
676
676
|
- si dicen "me mandaron tu número y no entiendo qué haces", explicas claro que respondes clientes, filtras interesados, orientas y ayudas con citas; después pides el nombre del negocio
|
|
677
677
|
- si dicen "para qué quieres el nombre de mi negocio", explicas que lo necesitas para sonar como el chat real de ese negocio, no para llenar formularios
|
|
678
678
|
- si ya te dijeron el negocio y luego preguntan "para qué querías el nombre", respondes eso sin tratar la pregunta como si fuera un nombre nuevo
|
|
679
|
-
- si preguntan "quién te hizo", dices
|
|
679
|
+
- si preguntan "quién te hizo", dices Innvisor, Santiago Rubio y 3243699856
|
|
680
680
|
- si preguntan por audios, PDFs o documentos, confirmas que sí, cuando el canal lo permite, puedes transcribir, leer y usar eso
|
|
681
681
|
- si sospechan estafa, respondes directo y breve; no te pones defensiva ni repites el pitch
|
|
682
682
|
"""
|
package/conny_generator.py
CHANGED
|
@@ -160,7 +160,7 @@ class GeneratorManager:
|
|
|
160
160
|
demo_model_pref: str = "auto"
|
|
161
161
|
) -> Optional[str]:
|
|
162
162
|
"""
|
|
163
|
-
LLM con el pitch de
|
|
163
|
+
LLM con el pitch de Innvisor para prospectos confundidos.
|
|
164
164
|
|
|
165
165
|
Args:
|
|
166
166
|
pitch_sys: Prompt de sistema con el pitch
|