@innvisor/conny-ai 9.7.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/.env.example +68 -0
- package/CHANGELOG.md +54 -0
- package/LICENSE +21 -0
- package/README.md +369 -0
- package/brand-assets/A_dark_luxury_web_background_202605210700.jpeg +0 -0
- package/brand-assets/Conny.web.logo.png +0 -0
- package/brand-assets/Logo_Conny_Petalo_Claro.png +0 -0
- package/brand-assets/cl-nica-de-las-am-ricas/manifest.json +22 -0
- package/brand-assets/cl-nica-de-las-am-ricas/processed/business-identity.txt +11 -0
- package/brand-assets/cl-nica-de-las-am-ricas/raw/business-identity.txt +11 -0
- package/brand-assets/cl-nica-las-am-ricas/manifest.json +22 -0
- package/brand-assets/cl-nica-las-am-ricas/processed/business-identity.txt +11 -0
- package/brand-assets/cl-nica-las-am-ricas/raw/business-identity.txt +11 -0
- package/brand-assets/conny-demo/manifest.json +22 -0
- package/brand-assets/conny-demo/processed/business-identity.txt +7 -0
- package/brand-assets/conny-demo/raw/business-identity.txt +7 -0
- package/brand-assets/conny-logo.png +0 -0
- package/brand-assets/web.background.png +0 -0
- package/brand_assets.py +323 -0
- package/conny +28 -0
- package/conny-chat.py +579 -0
- package/conny-omni.py +3843 -0
- package/conny.py +113 -0
- package/conny_agents/__init__.py +1 -0
- package/conny_agents/agenda.py +1 -0
- package/conny_agents/captacion.py +1 -0
- package/conny_agents/conocimiento.py +1 -0
- package/conny_agents/escalacion.py +1 -0
- package/conny_agents/objeciones.py +1 -0
- package/conny_agents/seguimiento.py +1 -0
- package/conny_app.py +287 -0
- package/conny_audio.py +350 -0
- package/conny_audio_learn.py +84 -0
- package/conny_brain_v10.py +804 -0
- package/conny_bridge.py +656 -0
- package/conny_calendar.py +169 -0
- package/conny_cli.py +11784 -0
- package/conny_cli_bb.py +437 -0
- package/conny_commands.py +243 -0
- package/conny_config.py +215 -0
- package/conny_core/__init__.py +3 -0
- package/conny_core/conversation_engine.py +446 -0
- package/conny_core/first_turn_ops.py +287 -0
- package/conny_core/persona_registry.py +157 -0
- package/conny_core/prompt_ops.py +561 -0
- package/conny_cron.py +72 -0
- package/conny_demo_v2.py +209 -0
- package/conny_demo_voice.py +134 -0
- package/conny_design.py +43 -0
- package/conny_doctor.py +319 -0
- package/conny_domino.py +696 -0
- package/conny_generator.py +447 -0
- package/conny_google_auth.py +159 -0
- package/conny_i18n.py +619 -0
- package/conny_init.py +509 -0
- package/conny_integrations/__init__.py +4 -0
- package/conny_integrations/llm.py +1 -0
- package/conny_integrations/vault.py +77 -0
- package/conny_integrations/whatsapp.py +1 -0
- package/conny_intelligence.py +65 -0
- package/conny_learning.py +154 -0
- package/conny_memory.py +243 -0
- package/conny_memory_engine.py +292 -0
- package/conny_nova_proxy.py +170 -0
- package/conny_nuke_robot_phrases.py +493 -0
- package/conny_pairing.py +253 -0
- package/conny_patch.py +291 -0
- package/conny_persona_cli.py +150 -0
- package/conny_router.py +308 -0
- package/conny_runtime_ops.py +271 -0
- package/conny_session.py +516 -0
- package/conny_skills/__init__.py +1 -0
- package/conny_skills/demo_mode.py +35 -0
- package/conny_skills/text_processing.py +1 -0
- package/conny_skills/tone_detection.py +1 -0
- package/conny_smart_features.py +333 -0
- package/conny_studio.py +161 -0
- package/conny_sync_fix.py +306 -0
- package/conny_tui.py +512 -0
- package/conny_tui_select.py +202 -0
- package/conny_ultra_config.py +411 -0
- package/conny_uncertainty.py +174 -0
- package/conny_utils.py +87 -0
- package/conny_voice.py +156 -0
- package/conny_voice_engine.py +124 -0
- package/conny_web_search.py +66 -0
- package/conny_weekly_report.py +85 -0
- package/conny_worm.py +88 -0
- package/core/__init__.py +25 -0
- package/ecosystem.config.js +24 -0
- package/fix_init.py +27 -0
- package/install.sh +78 -0
- package/knowledge_base.py +330 -0
- package/nova/rules/default.yaml +37 -0
- package/nova_bridge.py +509 -0
- package/npm/conny.js +471 -0
- package/package.json +102 -0
- package/personas/conny/base/default.yaml +35 -0
- package/personas/conny/base/estetica_whatsapp.yaml +36 -0
- package/requirements.txt +14 -0
- package/run.sh +47 -0
- package/search.py +465 -0
- package/smart_handoff.py +1150 -0
- package/src/__init__.py +0 -0
- package/src/conny/__init__.py +0 -0
- package/src/conny/admin/__init__.py +0 -0
- package/src/conny/admin/api.py +234 -0
- package/src/conny/admin/dashboard.py +772 -0
- package/src/conny/api/__init__.py +0 -0
- package/src/conny/api/routes.py +8851 -0
- package/src/conny/brain/__init__.py +15 -0
- package/src/conny/brain/engine.py +804 -0
- package/src/conny/brain/learning.py +154 -0
- package/src/conny/brain/memory.py +324 -0
- package/src/conny/brain/smart_features.py +333 -0
- package/src/conny/brain/uncertainty.py +167 -0
- package/src/conny/channels/__init__.py +0 -0
- package/src/conny/channels/audio.py +316 -0
- package/src/conny/channels/cli.py +11795 -0
- package/src/conny/channels/logo_art.py +11 -0
- package/src/conny/channels/voice.py +156 -0
- package/src/conny/core/__init__.py +0 -0
- package/src/conny/core/config.py +215 -0
- package/src/conny/core/cron.py +72 -0
- package/src/conny/core/messenger.py +563 -0
- package/src/conny/core/router.py +297 -0
- package/src/conny/core/session.py +312 -0
- package/src/conny/demo/__init__.py +0 -0
- package/src/conny/demo/handler.py +3110 -0
- package/src/conny/integrations/__init__.py +19 -0
- package/src/conny/integrations/calendar.py +169 -0
- package/src/conny/integrations/knowledge.py +312 -0
- package/src/conny/integrations/search.py +66 -0
- package/src/conny/personas/__init__.py +0 -0
- package/src/conny/personas/generator.py +447 -0
- package/src/conny/production/__init__.py +0 -0
- package/src/conny/production/domino.py +696 -0
- package/src/conny/production/guard.py +550 -0
- package/src/conny/production/handoff.py +1150 -0
- package/src/conny/production/monitor.py +353 -0
- package/src/conny/utils/__init__.py +2 -0
- package/src/conny/utils/helpers.py +75 -0
- package/src/conny/utils/i18n.py +619 -0
- package/src/core/admin_engines.py +772 -0
- package/src/core/globals.py +11845 -0
- package/src/core/orchestrator.py +273 -0
- package/src/core/production_monitor.py +353 -0
- package/src/core/runtime.py +5487 -0
- package/src/domain/onboarding_flow.py +230 -0
- package/src/domain/prompts/__init__.py +1 -0
- package/src/domain/prompts/prospect_pitch.py +282 -0
- package/src/domain/send_guard.py +636 -0
- package/src/domain/swarm/queen.py +96 -0
- package/src/infrastructure/llm_providers/engine.py +487 -0
- package/src/interfaces/mcp_server.py +73 -0
- package/src/interfaces/nova_bridge.py +58 -0
- package/src/interfaces/web/admin_api.py +1379 -0
- package/src/interfaces/web/app.py +9408 -0
- package/src/interfaces/web/demo_handler.py +3450 -0
- package/src/interfaces/web/static/generate_avatars.py +46 -0
- package/v7/__init__.py +46 -0
- package/v7/agents/__init__.py +46 -0
- package/v7/agents/agenda.py +77 -0
- package/v7/agents/base.py +216 -0
- package/v7/agents/captacion.py +60 -0
- package/v7/agents/conocimiento.py +69 -0
- package/v7/agents/escalacion.py +83 -0
- package/v7/agents/objeciones.py +109 -0
- package/v7/agents/seguimiento.py +71 -0
- package/v7/memory/__init__.py +46 -0
- package/v7/memory/patient_profile.py +200 -0
- package/v7/orchestrator.py +275 -0
- package/v7/postprocess.py +127 -0
- package/v7/router.py +239 -0
- package/verify_conversation_impl.py +48 -0
package/conny.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""Conny AI - Monolithic Bootstrap Facade."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
# Ensure project root is in sys.path
|
|
9
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
10
|
+
|
|
11
|
+
# Force full reload of submodules for dynamic test environments to maintain perfect isolation
|
|
12
|
+
if __name__ != "__main__":
|
|
13
|
+
for k in list(sys.modules.keys()):
|
|
14
|
+
if k.startswith("src.core") or k.startswith("src.interfaces"):
|
|
15
|
+
del sys.modules[k]
|
|
16
|
+
|
|
17
|
+
# 1. Import all globals dynamically
|
|
18
|
+
import src.core.globals as globals_module
|
|
19
|
+
globals_module.__facade_name__ = __name__
|
|
20
|
+
for name in dir(globals_module):
|
|
21
|
+
if not name.startswith("__"):
|
|
22
|
+
globals()[name] = getattr(globals_module, name)
|
|
23
|
+
|
|
24
|
+
# 2. Import all runtime attributes dynamically
|
|
25
|
+
import src.core.runtime as runtime_module
|
|
26
|
+
runtime_module.__facade_name__ = __name__
|
|
27
|
+
for name in dir(runtime_module):
|
|
28
|
+
if not name.startswith("__"):
|
|
29
|
+
globals()[name] = getattr(runtime_module, name)
|
|
30
|
+
|
|
31
|
+
# 3. Import all web app attributes dynamically
|
|
32
|
+
import src.interfaces.web.app as app_module
|
|
33
|
+
app_module.__facade_name__ = __name__
|
|
34
|
+
for name in dir(app_module):
|
|
35
|
+
if not name.startswith("__"):
|
|
36
|
+
globals()[name] = getattr(app_module, name)
|
|
37
|
+
|
|
38
|
+
# 4. Define conny global instance and initialization helpers
|
|
39
|
+
conny: ConnyUltra = None
|
|
40
|
+
ADMIN_PENDING_CONFIRMATIONS: Dict[str, Dict] = {}
|
|
41
|
+
db = None
|
|
42
|
+
llm_engine = None
|
|
43
|
+
anti_robot_filter = None
|
|
44
|
+
conversation_simulator = None
|
|
45
|
+
response_variation = None
|
|
46
|
+
hallucination_guard = None
|
|
47
|
+
owner_style_controller = None
|
|
48
|
+
auth_engine = None
|
|
49
|
+
mcp_manager = None
|
|
50
|
+
prompt_evolver = None
|
|
51
|
+
trainer_gateway = None
|
|
52
|
+
v8_build_quality_system_prompt_addon = None
|
|
53
|
+
trainer_get_system_prompt_addon = None
|
|
54
|
+
task_manager = None
|
|
55
|
+
v8_process_response = None
|
|
56
|
+
v8_process_agentic_intent = None
|
|
57
|
+
|
|
58
|
+
async def init_conny():
|
|
59
|
+
"""Inicializa Conny Ultra."""
|
|
60
|
+
global conny
|
|
61
|
+
conny = ConnyUltra()
|
|
62
|
+
await conny.initialize()
|
|
63
|
+
|
|
64
|
+
import src.core.globals as g
|
|
65
|
+
global db, llm_engine, auth_engine, mcp_manager
|
|
66
|
+
db = g.db
|
|
67
|
+
llm_engine = g.llm_engine
|
|
68
|
+
auth_engine = getattr(g, "auth_engine", None)
|
|
69
|
+
mcp_manager = getattr(g, "mcp_manager", None)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# 5. CLI entrypoint
|
|
73
|
+
if __name__ == "__main__":
|
|
74
|
+
import uvicorn
|
|
75
|
+
|
|
76
|
+
port = int(os.getenv("PORT", "8001"))
|
|
77
|
+
host = os.getenv("HOST", "0.0.0.0")
|
|
78
|
+
|
|
79
|
+
print("""
|
|
80
|
+
╔══════════════════════════════════════════════════════════════════╗
|
|
81
|
+
║ ║
|
|
82
|
+
║ ██████╗ ██████╗ ███╗ ██╗███╗ ██╗██╗ ██╗ ║
|
|
83
|
+
║ ██╔════╝ ██╔═══██╗████╗ ██║████╗ ██║╚██╗ ██╔╝ ║
|
|
84
|
+
║ ██║ ██║ ██║██╔██╗ ██║██╔██╗ ██║ ╚████╔╝ ║
|
|
85
|
+
║ ██║ ██║ ██║██║╚██╗██║██║╚██╗██║ ╚██╔╝ ║
|
|
86
|
+
║ ╚██████╗ ╚██████╔╝██║ ╚████║██║ ╚████║ ██║ ║
|
|
87
|
+
║ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═══╝ ╚═╝ ║
|
|
88
|
+
║ ║
|
|
89
|
+
║ U L T R A v 9 . 7 . 0 ║
|
|
90
|
+
║ ║
|
|
91
|
+
║ Agente de Recepción Hipernaturalmente Humana ║
|
|
92
|
+
║ ║
|
|
93
|
+
╠══════════════════════════════════════════════════════════════════╣
|
|
94
|
+
║ • AntiRobotFilter — elimina cada patrón de bot antes de enviar ║
|
|
95
|
+
║ • ConversationIntelligence — etapas, emociones, compromiso ║
|
|
96
|
+
║ • HyperHumanEngine — valida humanidad en cada respuesta ║
|
|
97
|
+
║ • SmartVariety — nunca repite apertura ni cierre igual ║
|
|
98
|
+
║ • /modelo — admin cambia el LLM en caliente ║
|
|
99
|
+
║ • Ortografía perfecta forzada (tildes, puntuación) ║
|
|
100
|
+
║ • PersonaEvolution — aprende el estilo de cada cliente ║
|
|
101
|
+
║ • ConversionFunnelTracker — sabe en qué etapa está cada lead ║
|
|
102
|
+
║ • Demo V2 — comportamiento idéntico al de producción ║
|
|
103
|
+
║ • MultilingualHandler — español/inglés/portugués automático ║
|
|
104
|
+
╚══════════════════════════════════════════════════════════════════╝
|
|
105
|
+
""")
|
|
106
|
+
|
|
107
|
+
uvicorn.run(
|
|
108
|
+
"conny:app",
|
|
109
|
+
host=host,
|
|
110
|
+
port=port,
|
|
111
|
+
reload=False,
|
|
112
|
+
log_level="info"
|
|
113
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Conny agents scaffold."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Agente de agenda scaffold."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Agente de captacion scaffold."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Agente de conocimiento scaffold."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Agente de escalacion scaffold."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Agente de objeciones scaffold."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Agente de seguimiento scaffold."""
|
package/conny_app.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""conny — AI receptionist platform."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os, sys, time, json, subprocess, signal, random
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
from rich import box
|
|
12
|
+
from rich.theme import Theme
|
|
13
|
+
from rich.padding import Padding
|
|
14
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
15
|
+
|
|
16
|
+
from conny_design import (
|
|
17
|
+
COLORS, LOGO_FULL, WORM_RESTING, SEP,
|
|
18
|
+
ICON_ONLINE, ICON_OFFLINE, ICON_OK, ICON_ERR, ICON_WARN, ICON_BRAND,
|
|
19
|
+
ICON_CORE, ICON_BOT, ICON_INT, ICON_OPS,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
THEME = Theme({
|
|
23
|
+
"m": f"bold {COLORS['primary']}",
|
|
24
|
+
"m.dim": COLORS['secondary'],
|
|
25
|
+
"ok": COLORS["success"],
|
|
26
|
+
"warn": COLORS["warning"],
|
|
27
|
+
"err": COLORS["error"],
|
|
28
|
+
"dim": COLORS["dim"],
|
|
29
|
+
"text": COLORS["text"],
|
|
30
|
+
})
|
|
31
|
+
con = Console(theme=THEME)
|
|
32
|
+
|
|
33
|
+
VERSION = "9.3.5"
|
|
34
|
+
try: VERSION = json.loads((Path(__file__).parent / "package.json").read_text()).get("version", VERSION)
|
|
35
|
+
except: pass
|
|
36
|
+
|
|
37
|
+
TAGLINES = [
|
|
38
|
+
"Enterprise AI Receptionist",
|
|
39
|
+
"Smart Conversational Interface",
|
|
40
|
+
"Omni-channel Business Intelligence",
|
|
41
|
+
"Autonomous Customer Engagement",
|
|
42
|
+
"Scalable AI Agent Infrastructure",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
BOOT_FILE = Path.home() / ".conny" / ".boot_shown"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ─── Boot ─────────────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
def _should_boot():
|
|
51
|
+
"""Show boot animation once per session."""
|
|
52
|
+
if not sys.stdout.isatty():
|
|
53
|
+
return False
|
|
54
|
+
if BOOT_FILE.exists():
|
|
55
|
+
ts = BOOT_FILE.read_text().strip()
|
|
56
|
+
if ts == time.strftime("%Y-%m-%d"):
|
|
57
|
+
return False
|
|
58
|
+
return True
|
|
59
|
+
|
|
60
|
+
def _mark_boot():
|
|
61
|
+
BOOT_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
62
|
+
BOOT_FILE.write_text(time.strftime("%Y-%m-%d"))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# ─── Help (main screen) ──────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
def cmd_help(args=""):
|
|
68
|
+
# Boot animation (first time today)
|
|
69
|
+
if _should_boot():
|
|
70
|
+
try:
|
|
71
|
+
from conny_worm import boot_sequence
|
|
72
|
+
boot_sequence(duration=1.8)
|
|
73
|
+
_mark_boot()
|
|
74
|
+
except Exception:
|
|
75
|
+
_mark_boot()
|
|
76
|
+
|
|
77
|
+
con.print()
|
|
78
|
+
con.print(LOGO_FULL)
|
|
79
|
+
con.print(f" {ICON_BRAND} v{VERSION} [dim]·[/dim] [m.dim]{random.choice(TAGLINES)}[/m.dim] {WORM_RESTING}")
|
|
80
|
+
con.print(SEP)
|
|
81
|
+
|
|
82
|
+
# Live status
|
|
83
|
+
procs = _pm2()
|
|
84
|
+
online = [p for p in procs if "conny" in p.get("name","") and p.get("pm2_env",{}).get("status")=="online"]
|
|
85
|
+
if online:
|
|
86
|
+
for p in online:
|
|
87
|
+
name = p["name"].replace("conny-","").replace("conny","main")
|
|
88
|
+
up = _uptime(p.get("pm2_env",{}).get("pm_uptime",0))
|
|
89
|
+
mem = p.get("monit",{}).get("memory",0)/1024/1024
|
|
90
|
+
con.print(f" {ICON_ONLINE} [bold]{name:30s}[/bold] [dim]{mem:.0f}M {up}[/dim]")
|
|
91
|
+
else:
|
|
92
|
+
con.print(f" {ICON_OFFLINE} [dim]No active Conny instances found[/dim]")
|
|
93
|
+
con.print(SEP)
|
|
94
|
+
|
|
95
|
+
# Commands
|
|
96
|
+
sections = [
|
|
97
|
+
(f"{ICON_CORE} CORE", [
|
|
98
|
+
("new", "Launch a new instance", "n"),
|
|
99
|
+
("list", "See all your agents", "l"),
|
|
100
|
+
("status", "Check vital signs", "s"),
|
|
101
|
+
("doctor", "Auto-heal & diagnostics", "d"),
|
|
102
|
+
("chat", "Talk to Conny (monitor)", "c"),
|
|
103
|
+
("logs", "Live brain stream", ""),
|
|
104
|
+
]),
|
|
105
|
+
(f"{ICON_BOT} CONTROL", [
|
|
106
|
+
("persona", "Design her personality", ""),
|
|
107
|
+
("demo", "Toggle simulation mode", ""),
|
|
108
|
+
("modelo", "Switch LLM provider", ""),
|
|
109
|
+
("config", "Deep settings", ""),
|
|
110
|
+
("sync", "Deploy latest upgrades", ""),
|
|
111
|
+
]),
|
|
112
|
+
(f"{ICON_INT} INTELLIGENCE", [
|
|
113
|
+
("aprender", "Teach new knowledge", ""),
|
|
114
|
+
("gaps", "Unanswered questions", ""),
|
|
115
|
+
("reporte", "Performance metrics", ""),
|
|
116
|
+
("studio", "Live session studio", ""),
|
|
117
|
+
]),
|
|
118
|
+
(f"{ICON_OPS} OPERATIONS", [
|
|
119
|
+
("restart", "Quick reboot", "r"),
|
|
120
|
+
("stop", "Shutdown instance", ""),
|
|
121
|
+
("backup", "Snapshot your agent", ""),
|
|
122
|
+
("bridge", "WhatsApp connection", ""),
|
|
123
|
+
("pair", "Connect Telegram bot", ""),
|
|
124
|
+
]),
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
for title, cmds in sections:
|
|
129
|
+
con.print(f" [dim]{title}[/dim]")
|
|
130
|
+
for cmd, desc, sc in cmds:
|
|
131
|
+
sc_text = f"[dim]{sc}[/dim]" if sc else ""
|
|
132
|
+
con.print(f" [m]{cmd:12s}[/m] [text]{desc:35s}[/text] {sc_text}")
|
|
133
|
+
con.print()
|
|
134
|
+
|
|
135
|
+
con.print(SEP)
|
|
136
|
+
con.print(f" [dim]shortcuts: n=new l=list s=status d=doctor c=chat r=restart[/dim]")
|
|
137
|
+
con.print(f" [dim]docs: github.com/sxrubyo/conny[/dim]")
|
|
138
|
+
con.print()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# ─── Commands ────────────────────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
def cmd_status(args=""):
|
|
144
|
+
t = Table(box=box.SIMPLE, border_style=COLORS["primary"], show_edge=False, padding=(0,1))
|
|
145
|
+
t.add_column("", width=3); t.add_column("instance", style="bold")
|
|
146
|
+
t.add_column("status"); t.add_column("mem", justify="right", style="dim")
|
|
147
|
+
t.add_column("up", justify="right", style="dim")
|
|
148
|
+
for p in _pm2():
|
|
149
|
+
if "conny" not in p.get("name",""): continue
|
|
150
|
+
st = p.get("pm2_env",{}).get("status","?")
|
|
151
|
+
mem = p.get("monit",{}).get("memory",0)/1024/1024
|
|
152
|
+
up = _uptime(p.get("pm2_env",{}).get("pm_uptime",0))
|
|
153
|
+
icon = ICON_ONLINE if st=="online" else ICON_OFFLINE
|
|
154
|
+
st_s = f"[ok]{st}[/ok]" if st=="online" else f"[err]{st}[/err]"
|
|
155
|
+
t.add_row(icon, p["name"], st_s, f"{mem:.0f}M", up)
|
|
156
|
+
con.print(); con.print(Padding(t,(0,2))); con.print()
|
|
157
|
+
|
|
158
|
+
def cmd_list(args=""):
|
|
159
|
+
idir = Path("/home/ubuntu/conny-instances")
|
|
160
|
+
if not idir.exists(): con.print(" [dim]no instances[/dim]"); return
|
|
161
|
+
t = Table(box=box.SIMPLE, border_style=COLORS["primary"], show_edge=False, padding=(0,1))
|
|
162
|
+
t.add_column("", width=3); t.add_column("name", style="bold")
|
|
163
|
+
t.add_column("port", style="dim"); t.add_column("sector", style="m.dim")
|
|
164
|
+
for d in sorted(idir.iterdir()):
|
|
165
|
+
if not d.is_dir() or not (d/".env").exists(): continue
|
|
166
|
+
port=sector=""
|
|
167
|
+
for l in (d/".env").read_text().splitlines():
|
|
168
|
+
if l.startswith("PORT="): port=l.split("=",1)[1]
|
|
169
|
+
if l.startswith("SECTOR="): sector=l.split("=",1)[1]
|
|
170
|
+
t.add_row("[m]⬡[/m]", d.name, f":{port}" if port else "-", sector or "-")
|
|
171
|
+
con.print(); con.print(Padding(t,(0,2))); con.print()
|
|
172
|
+
|
|
173
|
+
def cmd_doctor(args=""):
|
|
174
|
+
_py("conny_doctor.py", *(args.split() if args.strip() else []))
|
|
175
|
+
|
|
176
|
+
def cmd_new(args=""): _py("conny_init.py")
|
|
177
|
+
def cmd_chat(args=""): _py("conny_studio.py", "--instance", args.strip() or "default")
|
|
178
|
+
def cmd_persona(args=""): _py("conny_persona_cli.py", *(args.split() if args else ["list"]))
|
|
179
|
+
def cmd_logs(args=""): _sh("pm2","logs",args.strip() or "conny","--lines","30","--nostream")
|
|
180
|
+
def cmd_sync(args=""): _py("conny_cli.py","sync")
|
|
181
|
+
def cmd_demo(args=""): _py("conny_cli.py","demo",args or "")
|
|
182
|
+
def cmd_modelo(args=""): _py("conny_cli.py","modelo",args or "")
|
|
183
|
+
def cmd_restart(args=""): _sh("pm2","restart",args.strip() or "conny")
|
|
184
|
+
def cmd_stop(args=""): _sh("pm2","stop",args.strip() or "conny")
|
|
185
|
+
def cmd_backup(args=""): _py("conny_cli.py","backup",args or "")
|
|
186
|
+
def cmd_bridge(args=""): _py("conny_cli.py","bridge",args or "")
|
|
187
|
+
def cmd_pair(args=""): _py("conny_cli.py","pair",args or "")
|
|
188
|
+
def cmd_reporte(args=""): _py("conny_weekly_report.py",args or "default")
|
|
189
|
+
def cmd_gaps(args=""):
|
|
190
|
+
from datetime import datetime
|
|
191
|
+
f=Path("knowledge_gaps")/f"{datetime.now().strftime('%Y-%m-%d')}.jsonl"
|
|
192
|
+
if not f.exists(): con.print(f" {ICON_OK} [dim]no gaps[/dim]"); return
|
|
193
|
+
con.print()
|
|
194
|
+
for line in open(f):
|
|
195
|
+
g=json.loads(line)
|
|
196
|
+
con.print(f" {ICON_WARN} {g.get('user_msg','')[:55]} [dim]({g.get('confidence',0):.0%})[/dim]")
|
|
197
|
+
con.print()
|
|
198
|
+
def cmd_aprender(args=""):
|
|
199
|
+
if not args or ("→" not in args and "->" not in args):
|
|
200
|
+
con.print(f' [dim]uso: conny aprender "pregunta" → "respuesta"[/dim]'); return
|
|
201
|
+
sep="→" if "→" in args else "->"
|
|
202
|
+
q,a=args.split(sep,1); q=q.strip().strip('"'); a=a.strip().strip('"')
|
|
203
|
+
Path("teachings").mkdir(exist_ok=True)
|
|
204
|
+
with open("teachings/default.jsonl","a") as f:
|
|
205
|
+
f.write(json.dumps({"ts":time.time(),"question":q,"answer":a})+"\n")
|
|
206
|
+
con.print(f" {ICON_OK} [m]{q[:35]}[/m] → {a[:35]}")
|
|
207
|
+
def cmd_config(args=""):
|
|
208
|
+
argv = ["config"]
|
|
209
|
+
if args.strip():
|
|
210
|
+
argv.extend(args.split())
|
|
211
|
+
_py("conny_cli.py", *argv)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# ─── Router ──────────────────────────────────────────────────────────────────
|
|
215
|
+
|
|
216
|
+
CMDS = {
|
|
217
|
+
"help":cmd_help,"--help":cmd_help,"-h":cmd_help,"?":cmd_help,
|
|
218
|
+
"status":cmd_status,"s":cmd_status,
|
|
219
|
+
"list":cmd_list,"l":cmd_list,
|
|
220
|
+
"new":cmd_new,"init":cmd_new,"n":cmd_new,
|
|
221
|
+
"doctor":cmd_doctor,"d":cmd_doctor,"doc":cmd_doctor,
|
|
222
|
+
"chat":cmd_chat,"c":cmd_chat,"studio":cmd_chat,
|
|
223
|
+
"persona":cmd_persona,"logs":cmd_logs,"demo":cmd_demo,
|
|
224
|
+
"modelo":cmd_modelo,"sync":cmd_sync,"sincronizar":cmd_sync,
|
|
225
|
+
"config":cmd_config,"gaps":cmd_gaps,"aprender":cmd_aprender,
|
|
226
|
+
"reporte":cmd_reporte,"restart":cmd_restart,"r":cmd_restart,
|
|
227
|
+
"stop":cmd_stop,"backup":cmd_backup,"bridge":cmd_bridge,"pair":cmd_pair,
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
def route(cmd, args=""):
|
|
231
|
+
if cmd in ("--version","-v"): con.print(f" conny v{VERSION}"); return
|
|
232
|
+
fn = CMDS.get(cmd.lower())
|
|
233
|
+
if fn: fn(args)
|
|
234
|
+
else: _py("conny_cli.py", cmd, args)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
# ─── Onboarding ──────────────────────────────────────────────────────────────
|
|
238
|
+
|
|
239
|
+
def first_run():
|
|
240
|
+
return not Path(os.path.expanduser("~/.conny/initialized")).exists()
|
|
241
|
+
|
|
242
|
+
def onboard():
|
|
243
|
+
con.print(); con.print(LOGO_FULL)
|
|
244
|
+
con.print(f"\n [bold]First time setup.[/bold] [dim]~3 minutes.[/dim]\n")
|
|
245
|
+
cmd_new()
|
|
246
|
+
Path(os.path.expanduser("~/.conny")).mkdir(parents=True,exist_ok=True)
|
|
247
|
+
Path(os.path.expanduser("~/.conny/initialized")).write_text(time.strftime("%Y-%m-%d"))
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
# ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
def _pm2():
|
|
253
|
+
try:
|
|
254
|
+
r=subprocess.run(["pm2","jlist"],capture_output=True,text=True,timeout=5)
|
|
255
|
+
return json.loads(r.stdout)
|
|
256
|
+
except: return []
|
|
257
|
+
|
|
258
|
+
def _uptime(ms):
|
|
259
|
+
if not ms: return "-"
|
|
260
|
+
s=(time.time()*1000-ms)/1000
|
|
261
|
+
if s<60: return f"{int(s)}s"
|
|
262
|
+
if s<3600: return f"{int(s/60)}m"
|
|
263
|
+
if s<86400: return f"{int(s/3600)}h"
|
|
264
|
+
return f"{int(s/86400)}d"
|
|
265
|
+
|
|
266
|
+
def _py(*a):
|
|
267
|
+
try: subprocess.run([sys.executable]+[str(x) for x in a],cwd="/home/ubuntu/conny")
|
|
268
|
+
except Exception as e: con.print(f" [err]{e}[/err]")
|
|
269
|
+
|
|
270
|
+
def _sh(*a):
|
|
271
|
+
try: subprocess.run(list(a))
|
|
272
|
+
except Exception as e: con.print(f" [err]{e}[/err]")
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# ─── Entry ───────────────────────────────────────────────────────────────────
|
|
276
|
+
|
|
277
|
+
def main():
|
|
278
|
+
signal.signal(signal.SIGINT, lambda *_: sys.exit(0))
|
|
279
|
+
if first_run() and not (len(sys.argv)>1 and sys.argv[1] in ("help","--help","-h","-v","--version")):
|
|
280
|
+
onboard()
|
|
281
|
+
if len(sys.argv) <= 1:
|
|
282
|
+
cmd_help()
|
|
283
|
+
else:
|
|
284
|
+
route(sys.argv[1], " ".join(sys.argv[2:]))
|
|
285
|
+
|
|
286
|
+
if __name__ == "__main__":
|
|
287
|
+
main()
|