- Renamed all references from maxclaw to langcode across all files - Renamed maxclaw.py → langcode.py - Renamed completion scripts: maxclaw.bash/zsh → langcode.bash/zsh - Updated Dockerfile, README, all Python files, HTML, JSON, YAML - New Gitea repository: http://192.168.0.183:8085/truetype/langcode_agent.git
552 lines
20 KiB
Python
Executable File
552 lines
20 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
LangCode Agent - Unified CLI Entry Point
|
|
Usage:
|
|
langcode → avvia l'agent (chat interattiva)
|
|
langcode chat → avvia l'agent
|
|
langcode server [--port N] → avvia web server
|
|
langcode tui → apre TUI configurazione
|
|
langcode config list → lista configurazioni
|
|
langcode config add → aggiungi configurazione
|
|
langcode config remove NAME → rimuovi configurazione
|
|
langcode config use NAME → attiva configurazione
|
|
langcode config active → mostra configurazione attiva
|
|
langcode config test [NAME] → testa connessione
|
|
langcode config export → esporta configurazioni
|
|
langcode config import FILE → importa configurazioni
|
|
langcode config search Q → cerca configurazioni
|
|
langcode analytics → mostra analytics
|
|
langcode dashboard → mostra dashboard performance
|
|
langcode version → mostra versione
|
|
"""
|
|
import sys
|
|
import os
|
|
import argparse
|
|
|
|
# Add current directory to path
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
VERSION = "1.0.0"
|
|
|
|
|
|
def cmd_chat(args):
|
|
"""Avvia l'agent in modalità chat interattiva"""
|
|
from main import create_langcode_agent, process_command
|
|
from langchain_core.messages import HumanMessage
|
|
|
|
print("🚀 Inizializzo LangCode Agent...")
|
|
|
|
try:
|
|
agent = create_langcode_agent()
|
|
print("✅ Agent inizializzato con successo!")
|
|
|
|
print("\n🔧 Strumenti disponibili:")
|
|
print(" • Provider tools: list models, test connections, set defaults")
|
|
print(" • Proxmox tools: manage VMs, snapshots, resources")
|
|
print(" • Standard tools: planning, filesystem, shell, subagents")
|
|
|
|
print("\n💬 Puoi interagire con l'agent.")
|
|
print("📌 Comandi speciali (prefissati con /):")
|
|
print(" /help - Mostra i comandi disponibili")
|
|
print(" /status - Mostra lo stato del sistema")
|
|
print(" /config - Gestisci le configurazioni")
|
|
print(" /exit - Esci dall'agente")
|
|
print("\n💡 Per chattare, scrivi semplicemente il tuo messaggio.")
|
|
|
|
while True:
|
|
try:
|
|
user_input = input("\n👤 You: ").strip()
|
|
except (EOFError, KeyboardInterrupt):
|
|
print("\n👋 Arrivederci!")
|
|
break
|
|
|
|
if not user_input:
|
|
continue
|
|
|
|
# Check if it's a command (starts with /)
|
|
if process_command(user_input):
|
|
continue
|
|
|
|
# Otherwise, treat it as a chat message
|
|
try:
|
|
result = agent.invoke({"messages": [HumanMessage(content=user_input)]})
|
|
ai_message = result["messages"][-1]
|
|
print(f"\n🤖 LangCode: {ai_message.content}")
|
|
except Exception as e:
|
|
print(f"\n❌ Errore: {str(e)}")
|
|
|
|
except Exception as e:
|
|
print(f"❌ Inizializzazione fallita: {str(e)}")
|
|
sys.exit(1)
|
|
|
|
|
|
def cmd_server(args):
|
|
"""Avvia il web server"""
|
|
import uvicorn
|
|
from api_server import app
|
|
|
|
host = args.host or "0.0.0.0"
|
|
port = args.port or 8000
|
|
|
|
print(f"🌐 Avvio LangCode Web Server su http://{host}:{port}")
|
|
print(f"📊 Dashboard: http://{host}:{port}/monitoring")
|
|
print(f"🛒 Marketplace: http://{host}:{port}/marketplace")
|
|
print(f"📡 API Docs: http://{host}:{port}/docs")
|
|
print("Premi Ctrl+C per fermare il server\n")
|
|
|
|
uvicorn.run(app, host=host, port=port)
|
|
|
|
|
|
def cmd_tui(args):
|
|
"""Apre la TUI per gestione configurazioni"""
|
|
from config_tui import main as tui_main
|
|
tui_main()
|
|
|
|
|
|
def cmd_config_list(args):
|
|
"""Lista tutte le configurazioni"""
|
|
from config_cli import print_configurations
|
|
from config_manager import config_manager
|
|
|
|
configs = config_manager.get_configurations()
|
|
if args.search:
|
|
from config_cli import filter_configurations
|
|
configs = filter_configurations(configs, args.search)
|
|
print_configurations(configs, title=f"Configurazioni trovate per '{args.search}'")
|
|
else:
|
|
print_configurations(configs)
|
|
|
|
|
|
def cmd_config_add(args):
|
|
"""Aggiunge una nuova configurazione"""
|
|
from config_manager import config_manager
|
|
|
|
if args.name and args.provider:
|
|
# Modalità non-interattiva
|
|
config_data = {"provider": args.provider}
|
|
|
|
if args.model:
|
|
config_data["model_name"] = args.model
|
|
else:
|
|
# Default model per provider
|
|
defaults = {
|
|
"openai": "gpt-4o",
|
|
"ollama": "gemma2:9b",
|
|
"openrouter": "anthropic/claude-3.5-sonnet",
|
|
"anthropic": "claude-3-5-sonnet-20241022",
|
|
"google": "gemini-1.5-pro"
|
|
}
|
|
config_data["model_name"] = defaults.get(args.provider, "gpt-4o")
|
|
|
|
if args.api_key:
|
|
config_data["api_key"] = args.api_key
|
|
if args.base_url:
|
|
config_data["base_url"] = args.base_url
|
|
|
|
try:
|
|
config_manager.add_configuration(args.name, config_data)
|
|
print(f"✅ Configurazione '{args.name}' aggiunta con successo!")
|
|
if args.activate:
|
|
config_manager.set_active_configuration(args.name)
|
|
print(f"✅ Configurazione '{args.name}' attivata!")
|
|
except Exception as e:
|
|
print(f"❌ Errore: {e}")
|
|
sys.exit(1)
|
|
else:
|
|
# Modalità interattiva
|
|
from config_cli import add_configuration
|
|
add_configuration()
|
|
|
|
|
|
def cmd_config_remove(args):
|
|
"""Rimuove una configurazione"""
|
|
from config_manager import config_manager
|
|
|
|
name = args.name
|
|
if not name:
|
|
name = input("Nome configurazione da rimuovere: ").strip()
|
|
|
|
if not name:
|
|
print("❌ Nome richiesto.")
|
|
sys.exit(1)
|
|
|
|
try:
|
|
config_manager.remove_configuration(name)
|
|
print(f"✅ Configurazione '{name}' rimossa!")
|
|
except Exception as e:
|
|
print(f"❌ Errore: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
def cmd_config_use(args):
|
|
"""Attiva una configurazione"""
|
|
from config_manager import config_manager
|
|
|
|
try:
|
|
config_manager.set_active_configuration(args.name)
|
|
print(f"✅ Configurazione '{args.name}' attivata!")
|
|
except Exception as e:
|
|
print(f"❌ Errore: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
def cmd_config_active(args):
|
|
"""Mostra la configurazione attiva"""
|
|
from config_manager import config_manager
|
|
|
|
active = config_manager.get_active_configuration()
|
|
if active:
|
|
config = config_manager.get_configuration(active)
|
|
print(f"\n📌 Configurazione attiva: {active}")
|
|
print(f" Provider: {config.get('provider', 'unknown')}")
|
|
print(f" Model: {config.get('model_name', 'unknown')}")
|
|
if config.get('base_url'):
|
|
print(f" Base URL: {config.get('base_url')}")
|
|
else:
|
|
print("⚠️ Nessuna configurazione attiva.")
|
|
|
|
|
|
def cmd_config_test(args):
|
|
"""Testa una configurazione"""
|
|
from config_manager import config_manager
|
|
|
|
name = args.name or config_manager.get_active_configuration()
|
|
if not name:
|
|
print("❌ Nessuna configurazione specificata o attiva.")
|
|
sys.exit(1)
|
|
|
|
config = config_manager.get_configuration(name)
|
|
if not config:
|
|
print(f"❌ Configurazione '{name}' non trovata.")
|
|
sys.exit(1)
|
|
|
|
print(f"🔍 Test connessione per '{name}'...")
|
|
provider = config.get("provider", "")
|
|
|
|
try:
|
|
if provider == "ollama":
|
|
import requests
|
|
base_url = config.get("base_url", "http://localhost:11434")
|
|
resp = requests.get(f"{base_url}/api/tags", timeout=10)
|
|
if resp.status_code == 200:
|
|
models = resp.json().get("models", [])
|
|
print(f"✅ Ollama connesso! {len(models)} modelli disponibili.")
|
|
for m in models[:5]:
|
|
print(f" • {m['name']}")
|
|
else:
|
|
print(f"❌ Errore: HTTP {resp.status_code}")
|
|
elif provider in ("openai", "openrouter", "anthropic"):
|
|
api_key = config.get("api_key", "")
|
|
if not api_key:
|
|
print("❌ API key non configurata.")
|
|
sys.exit(1)
|
|
print("✅ API key presente (test completo richiede una chiamata API)")
|
|
elif provider == "google":
|
|
api_key = config.get("api_key", "")
|
|
if not api_key:
|
|
print("❌ API key non configurata.")
|
|
sys.exit(1)
|
|
print("✅ API key presente (test completo richiede una chiamata API)")
|
|
else:
|
|
print(f"⚠️ Test non supportato per provider '{provider}'")
|
|
except Exception as e:
|
|
print(f"❌ Errore connessione: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
def cmd_config_export(args):
|
|
"""Esporta configurazioni"""
|
|
from config_cli import export_configurations
|
|
if args.format and args.output:
|
|
import json
|
|
import yaml
|
|
from pathlib import Path
|
|
from config_manager import config_manager
|
|
|
|
configs = config_manager.get_configurations()
|
|
if not configs:
|
|
print("❌ Nessuna configurazione da esportare.")
|
|
return
|
|
|
|
try:
|
|
if args.format == 'json':
|
|
export_file = Path(f"{args.output}.json")
|
|
with open(export_file, 'w') as f:
|
|
json.dump(configs, f, indent=2)
|
|
else:
|
|
export_file = Path(f"{args.output}.yaml")
|
|
with open(export_file, 'w') as f:
|
|
yaml.dump(configs, f, default_flow_style=False)
|
|
print(f"✅ Configurazioni esportate in {export_file}")
|
|
except Exception as e:
|
|
print(f"❌ Errore: {e}")
|
|
sys.exit(1)
|
|
else:
|
|
export_configurations()
|
|
|
|
|
|
def cmd_config_import(args):
|
|
"""Importa configurazioni"""
|
|
if args.file:
|
|
import json
|
|
import yaml
|
|
from pathlib import Path
|
|
from config_manager import config_manager
|
|
|
|
file_path = Path(args.file)
|
|
if not file_path.exists():
|
|
print(f"❌ File '{args.file}' non trovato.")
|
|
sys.exit(1)
|
|
|
|
try:
|
|
with open(file_path, 'r') as f:
|
|
if args.file.endswith('.json'):
|
|
imported = json.load(f)
|
|
elif args.file.endswith(('.yaml', '.yml')):
|
|
imported = yaml.safe_load(f)
|
|
else:
|
|
print("❌ Formato non supportato. Usa .json o .yaml")
|
|
sys.exit(1)
|
|
|
|
if not isinstance(imported, dict):
|
|
print("❌ Formato configurazione non valido.")
|
|
sys.exit(1)
|
|
|
|
count = 0
|
|
for name, config in imported.items():
|
|
try:
|
|
config_manager.add_configuration(name, config)
|
|
count += 1
|
|
print(f"✅ Importata: {name}")
|
|
except Exception as e:
|
|
print(f"⚠️ '{name}': {e}")
|
|
|
|
print(f"\n✅ {count} configurazioni importate.")
|
|
except Exception as e:
|
|
print(f"❌ Errore: {e}")
|
|
sys.exit(1)
|
|
else:
|
|
from config_cli import import_configurations
|
|
import_configurations()
|
|
|
|
|
|
def cmd_config_search(args):
|
|
"""Cerca configurazioni"""
|
|
from config_cli import print_configurations, filter_configurations
|
|
from config_manager import config_manager
|
|
|
|
configs = config_manager.get_configurations()
|
|
filtered = filter_configurations(configs, args.query)
|
|
print_configurations(filtered, title=f"Risultati per '{args.query}'")
|
|
|
|
|
|
def cmd_analytics(args):
|
|
"""Mostra analytics"""
|
|
from analytics import AnalyticsEngine
|
|
|
|
engine = AnalyticsEngine()
|
|
|
|
if args.report:
|
|
report = engine.get_detailed_report()
|
|
print(report)
|
|
elif args.clear:
|
|
engine.clear_old_data()
|
|
print("✅ Dati analytics cancellati.")
|
|
else:
|
|
# Mostra riepilogo rapido
|
|
summary = engine.get_usage_summary()
|
|
insights = engine.get_insights()
|
|
|
|
print("\n📊 LangCode Analytics")
|
|
print("=" * 40)
|
|
|
|
if summary.get("total_conversations", 0) > 0:
|
|
print(f"\n💬 Conversazioni totali: {summary['total_conversations']}")
|
|
print(f"📝 Messaggi totali: {summary.get('total_messages', 0)}")
|
|
print(f"⏱️ Tempo medio risposta: {summary.get('avg_response_time', 0):.2f}s")
|
|
|
|
if summary.get("provider_usage"):
|
|
print("\n🔌 Utilizzo provider:")
|
|
for provider, count in summary["provider_usage"].items():
|
|
print(f" • {provider}: {count} volte")
|
|
|
|
if summary.get("tool_usage"):
|
|
print("\n🔧 Utilizzo strumenti:")
|
|
for tool, count in sorted(summary["tool_usage"].items(), key=lambda x: -x[1])[:5]:
|
|
print(f" • {tool}: {count} volte")
|
|
else:
|
|
print("\n📭 Nessun dato analytics ancora.")
|
|
print(" Usa l'agent per generare dati!")
|
|
|
|
if insights:
|
|
print("\n💡 Insights:")
|
|
for insight in insights[:3]:
|
|
print(f" • {insight}")
|
|
|
|
print(f"\n📁 Dati salvati in: {engine.data_dir}")
|
|
print(" Usa 'langcode analytics --report' per report completo")
|
|
|
|
|
|
def cmd_dashboard(args):
|
|
"""Mostra dashboard performance"""
|
|
from dashboard import PerformanceDashboard
|
|
|
|
dashboard = PerformanceDashboard()
|
|
|
|
if args.clear:
|
|
dashboard.clear_old_data()
|
|
print("✅ Dati performance cancellati.")
|
|
else:
|
|
report = dashboard.get_summary()
|
|
print("\n📈 LangCode Performance Dashboard")
|
|
print("=" * 40)
|
|
|
|
if report.get("total_requests", 0) > 0:
|
|
print(f"\n📊 Richieste totali: {report['total_requests']}")
|
|
print(f"⏱️ Tempo medio risposta: {report.get('avg_response_time', 0):.2f}s")
|
|
print(f"✅ Tasso successo: {report.get('success_rate', 0):.1f}%")
|
|
|
|
if report.get("slowest_operations"):
|
|
print("\n🐌 Operazioni più lente:")
|
|
for op in report["slowest_operations"][:3]:
|
|
print(f" • {op['name']}: {op['time']:.2f}s")
|
|
|
|
if report.get("memory_usage"):
|
|
mem = report["memory_usage"]
|
|
print(f"\n💾 Memoria: {mem.get('current', 0):.1f}MB (picco: {mem.get('peak', 0):.1f}MB)")
|
|
else:
|
|
print("\n📭 Nessun dato performance ancora.")
|
|
|
|
print(f"\n📁 Dati salvati in: {dashboard.data_dir}")
|
|
|
|
|
|
def cmd_version(args):
|
|
"""Mostra versione"""
|
|
print(f"LangCode Agent v{VERSION}")
|
|
print("Agent harness basato su LangChain Deep Agents")
|
|
print("https://github.com/truetype/langcode_agent")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
prog="langcode",
|
|
description="🚀 LangCode Agent - AI Agent harness configurabile",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Esempi:
|
|
langcode Avvia l'agent (chat interattiva)
|
|
langcode server --port 3000 Avvia web server su porta 3000
|
|
langcode config list Lista tutte le configurazioni
|
|
langcode config add -n ollama -p ollama -m gemma2:9b
|
|
langcode config use ollama Attiva configurazione 'ollama'
|
|
langcode config test Testa configurazione attiva
|
|
langcode analytics Mostra riepilogo analytics
|
|
langcode analytics --report Genera report completo
|
|
"""
|
|
)
|
|
parser.add_argument("--version", "-v", action="version", version=f"langcode {VERSION}")
|
|
|
|
subparsers = parser.add_subparsers(dest="command", help="Comandi disponibili")
|
|
|
|
# === CHAT (default) ===
|
|
chat_parser = subparsers.add_parser("chat", help="Avvia l'agent in modalità chat")
|
|
chat_parser.set_defaults(func=cmd_chat)
|
|
|
|
# === SERVER ===
|
|
server_parser = subparsers.add_parser("server", help="Avvia il web server")
|
|
server_parser.add_argument("--port", "-p", type=int, default=8000, help="Porta del server (default: 8000)")
|
|
server_parser.add_argument("--host", "-H", default="0.0.0.0", help="Host del server (default: 0.0.0.0)")
|
|
server_parser.set_defaults(func=cmd_server)
|
|
|
|
# === TUI ===
|
|
tui_parser = subparsers.add_parser("tui", help="Apre TUI per gestione configurazioni")
|
|
tui_parser.set_defaults(func=cmd_tui)
|
|
|
|
# === CONFIG (nested subcommands) ===
|
|
config_parser = subparsers.add_parser("config", help="Gestione configurazioni")
|
|
config_subparsers = config_parser.add_subparsers(dest="config_command", help="Sottocomandi config")
|
|
|
|
# config list
|
|
cl = config_subparsers.add_parser("list", help="Lista configurazioni")
|
|
cl.add_argument("--search", "-s", help="Filtra per termine di ricerca")
|
|
cl.set_defaults(func=cmd_config_list)
|
|
|
|
# config add
|
|
ca = config_subparsers.add_parser("add", help="Aggiungi configurazione")
|
|
ca.add_argument("--name", "-n", help="Nome configurazione")
|
|
ca.add_argument("--provider", "-p", choices=["openai", "ollama", "openrouter", "anthropic", "google"], help="Provider")
|
|
ca.add_argument("--model", "-m", help="Nome modello")
|
|
ca.add_argument("--api-key", "-k", help="API key")
|
|
ca.add_argument("--base-url", "-u", help="Base URL")
|
|
ca.add_argument("--activate", "-a", action="store_true", help="Attiva dopo la creazione")
|
|
ca.set_defaults(func=cmd_config_add)
|
|
|
|
# config remove
|
|
cr = config_subparsers.add_parser("remove", help="Rimuovi configurazione")
|
|
cr.add_argument("name", help="Nome configurazione da rimuovere")
|
|
cr.set_defaults(func=cmd_config_remove)
|
|
|
|
# config use
|
|
cu = config_subparsers.add_parser("use", help="Attiva configurazione")
|
|
cu.add_argument("name", help="Nome configurazione da attivare")
|
|
cu.set_defaults(func=cmd_config_use)
|
|
|
|
# config active
|
|
cv = config_subparsers.add_parser("active", help="Mostra configurazione attiva")
|
|
cv.set_defaults(func=cmd_config_active)
|
|
|
|
# config test
|
|
ct = config_subparsers.add_parser("test", help="Testa connessione configurazione")
|
|
ct.add_argument("name", nargs="?", help="Nome configurazione (default: attiva)")
|
|
ct.set_defaults(func=cmd_config_test)
|
|
|
|
# config export
|
|
ce = config_subparsers.add_parser("export", help="Esporta configurazioni")
|
|
ce.add_argument("--format", "-f", choices=["json", "yaml"], help="Formato export")
|
|
ce.add_argument("--output", "-o", help="Nome file output (senza estensione)")
|
|
ce.set_defaults(func=cmd_config_export)
|
|
|
|
# config import
|
|
ci = config_subparsers.add_parser("import", help="Importa configurazioni")
|
|
ci.add_argument("file", nargs="?", help="File da importare (.json o .yaml)")
|
|
ci.set_defaults(func=cmd_config_import)
|
|
|
|
# config search
|
|
cs = config_subparsers.add_parser("search", help="Cerca configurazioni")
|
|
cs.add_argument("query", help="Termine di ricerca")
|
|
cs.set_defaults(func=cmd_config_search)
|
|
|
|
# === ANALYTICS ===
|
|
analytics_parser = subparsers.add_parser("analytics", help="Mostra analytics")
|
|
analytics_parser.add_argument("--report", "-r", action="store_true", help="Genera report completo")
|
|
analytics_parser.add_argument("--clear", "-c", action="store_true", help="Cancella dati analytics")
|
|
analytics_parser.set_defaults(func=cmd_analytics)
|
|
|
|
# === DASHBOARD ===
|
|
dashboard_parser = subparsers.add_parser("dashboard", help="Mostra dashboard performance")
|
|
dashboard_parser.add_argument("--clear", "-c", action="store_true", help="Cancella dati performance")
|
|
dashboard_parser.set_defaults(func=cmd_dashboard)
|
|
|
|
# === VERSION ===
|
|
version_parser = subparsers.add_parser("version", help="Mostra versione")
|
|
version_parser.set_defaults(func=cmd_version)
|
|
|
|
# Parse arguments
|
|
args = parser.parse_args()
|
|
|
|
# Se nessun comando, avvia chat (default)
|
|
if not args.command:
|
|
args.func = cmd_chat
|
|
args.func(args)
|
|
return
|
|
|
|
# Esegui il comando
|
|
if hasattr(args, 'func'):
|
|
args.func(args)
|
|
else:
|
|
parser.print_help()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|