Compare commits

..
10 Commits
Author SHA1 Message Date
truetype eda22811ce Rename project: maxclaw → langcode
- 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
2026-03-21 08:36:35 +01:00
truetype 0612b290d5 Phase 19: Unified CLI entry point (maxclaw.py)
- Single 'maxclaw' command for all functionality
- Subcommands: chat, server, tui, config, analytics, dashboard, version
- Nested config subcommands: list, add, remove, use, active, test, export, import, search
- Updated README with unified CLI documentation
- Updated Dockerfile to use maxclaw.py
- Added .gitignore for __pycache__
2026-03-21 08:03:47 +01:00
truetype 3e986d83f1 Phase 18: Advanced analytics and enhanced documentation 2026-03-20 21:10:58 +01:00
truetype 1dae4c5656 Phase 17: Plugin marketplace and enhanced documentation 2026-03-20 20:55:34 +01:00
truetype cf8e4c677f Phase 16: Responsive web design improvements and enhanced documentation 2026-03-20 20:34:23 +01:00
truetype b3f57a8635 Phase 15: Multi-language support for monitoring dashboard 2026-03-20 20:19:31 +01:00
truetype 7103b218b9 Phase 14: Enhanced performance optimizations with connection pooling and lazy loading 2026-03-20 20:01:27 +01:00
truetype 979d19bdc1 Phase 13: Real-time monitoring web interface and enhanced API server 2026-03-20 19:46:41 +01:00
truetype e92cc701f6 Phase 12: Additional plugin examples (File Tools and System Utils) 2026-03-20 19:31:53 +01:00
truetype 6e1b1a44db Phase 11: Performance monitoring dashboard and enhanced documentation 2026-03-20 19:17:07 +01:00
40 changed files with 5578 additions and 182 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
__pycache__/
+4 -4
View File
@@ -1,4 +1,4 @@
# MaxClaw Agent Docker Image
# LangCode Agent Docker Image
FROM python:3.11-slim
# Set working directory
@@ -20,8 +20,8 @@ RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Create directory for configuration
RUN mkdir -p /root/.maxclaw && \
chmod 700 /root/.maxclaw
RUN mkdir -p /root/.langcode && \
chmod 700 /root/.langcode
# Expose port for API (future use)
EXPOSE 8000
@@ -33,4 +33,4 @@ ENV MODEL_NAME=gemma2:9b
ENV OLLAMA_BASE_URL=http://host.docker.internal:11434
# Default command
CMD ["python", "main.py"]
CMD ["python", "langcode.py"]
+527 -28
View File
@@ -1,4 +1,4 @@
# MaxClaw Agent
# LangCode Agent
Un agente configurabile basato su DeepAgents che supporta più provider LLM.
@@ -15,11 +15,18 @@ Un agente configurabile basato su DeepAgents che supporta più provider LLM.
- **Test completi**: Suite di test per garantire la qualità del codice
- **Docker support**: Containerizzazione per deployment facile
- **API REST**: Server API per accesso remoto
- **Web UI**: Interfaccia web per gestione configurazioni
- **Web UI responsive**: Interfaccia web per gestione configurazioni ottimizzata per mobile e desktop
- **Dashboard monitoraggio**: Monitoraggio in tempo reale del sistema con supporto multilingue
- **Marketplace plugin**: Scopri e installa plugin dal marketplace
- **Analytics avanzate**: Analisi dettagliate dell'uso e raccomandazioni
- **Comandi integrati**: Comandi speciali con prefisso "/" per gestione rapida
- **Monitoraggio**: Sistema di logging e monitoraggio delle performance
- **Dashboard performance**: Monitoraggio dettagliato delle performance
- **Ottimizzazioni performance**: Caching, connection pooling, lazy loading
- **Sistema di plugin**: Estendi le funzionalità con plugin personalizzati
- **Supporto multilingue**: Interfaccia in italiano e inglese
- **Plugin inclusi**: File tools, System utils, Example plugin
- **Supporto multilingue**: Interfaccia in italiano e inglese per CLI, dashboard e web UI
- **Design responsive**: Interfacce web ottimizzate per dispositivi mobili e desktop
## Installazione
@@ -35,7 +42,7 @@ Un agente configurabile basato su DeepAgents che supporta più provider LLM.
```bash
# Build dell'immagine
docker build -t maxclaw-agent .
docker build -t langcode-agent .
# Esecuzione con Docker Compose
docker-compose up -d
@@ -45,7 +52,7 @@ docker run -it --rm \
-e MODEL_PROVIDER=ollama \
-e MODEL_NAME=gemma2:9b \
-e OLLAMA_BASE_URL=http://host.docker.internal:11434 \
maxclaw-agent
langcode-agent
```
### Configurazione del Completamento Shell
@@ -53,19 +60,19 @@ docker run -it --rm \
#### Bash
```bash
# Aggiungi al tuo ~/.bashrc o ~/.bash_profile
source /path/to/maxclaw_agent/completions/maxclaw.bash
source /path/to/langcode_agent/completions/langcode.bash
```
#### Zsh
```bash
# Aggiungi al tuo ~/.zshrc
fpath=(/path/to/maxclaw_agent/completions $fpath)
fpath=(/path/to/langcode_agent/completions $fpath)
autoload -Uz compinit && compinit
```
## Configurazione
Puoi configurare MaxClaw Agent in diversi modi:
Puoi configurare LangCode Agent in diversi modi:
### 1. Variabili d'ambiente (Avvio Rapido)
@@ -178,6 +185,12 @@ python api_server.py
# Accedi all'interfaccia web
# http://localhost:8000
# Accedi alla dashboard di monitoraggio
# http://localhost:8000/monitoring
# Accedi al marketplace dei plugin
# http://localhost:8000/marketplace
```
L'interfaccia web include:
@@ -185,12 +198,51 @@ L'interfaccia web include:
- Chat con l'agente
- Monitoraggio dello stato del sistema
- Test di connessione
- Dashboard di monitoraggio in tempo reale con supporto multilingue
- **Design responsive**: Ottimizzata per dispositivi mobili e desktop
- **Marketplace plugin**: Scopri e installa plugin dal marketplace
## Utilizzo
### Utilizzo Locale
### CLI Unificata (langcode)
Esegui l'agente:
Tutte le funzionalità sono accessibili tramite un unico comando `langcode`:
```bash
# Avvia l'agent (chat interattiva)
langcode
langcode chat
# Avvia web server
langcode server
langcode server --port 3000
# Gestione configurazioni
langcode config list # Lista configurazioni
langcode config add -n ollama -p ollama -m gemma2:9b -u http://192.168.0.50:11434
langcode config use ollama # Attiva configurazione
langcode config active # Mostra configurazione attiva
langcode config test # Testa connessione
langcode config export -f json -o backup
langcode config import backup.json
langcode config search ollama # Cerca configurazioni
# TUI interattiva
langcode tui
# Analytics e monitoraggio
langcode analytics # Riepilogo rapido
langcode analytics --report # Report completo
langcode dashboard # Dashboard performance
# Info
langcode version
langcode --help
```
### Utilizzo Locale (alternativo)
Esegui l'agente direttamente:
```bash
python main.py
```
@@ -209,7 +261,11 @@ Quando interagisci con l'agente, puoi usare comandi speciali preceduti da "/" pe
/models - Elenca i modelli disponibili per il provider attivo
/clear - Pulisce lo schermo
/plugins - Elenca i plugin disponibili
/marketplace - Apre il marketplace dei plugin
/analytics - Mostra il report analytics
/language - Cambia la lingua dell'interfaccia
/performance - Mostra il report delle performance
/monitoring - Apre la dashboard di monitoraggio
/exit, /quit - Esci dall'agente
```
@@ -227,10 +283,30 @@ Ollama URL: http://192.168.0.50:11434
openai-gpt4: openai - gpt-4o
👤 You: Ciao, chi sei?
🤖 MaxClaw: Ciao! Sono MaxClaw, un agente AI configurabile...
🤖 LangCode: Ciao! Sono LangCode, un agente AI configurabile...
👤 You: Elenca i file nella directory corrente
🤖 MaxClaw: [Esegue ls e mostra i file]
🤖 LangCode: [Esegue ls e mostra i file]
👤 You: /analytics
📊 Advanced Analytics Report (Last 30 days)
============================================================
💬 Conversation Statistics:
Total Conversations: 45
Total Message Characters: 12,500
Total Response Characters: 25,800
Average Message Length: 278 chars
Average Response Length: 573 chars
🤖 Provider Usage:
ollama: 30 (66.7%)
openai: 15 (33.3%)
Most Used: ollama
💡 Insights and Recommendations:
💡 You're heavily using ollama (67% of conversations). Consider diversifying with other providers for different use cases.
✅ Great response times! Average is 0.8s.
👤 You: /exit
👋 Goodbye!
@@ -243,16 +319,18 @@ Ollama URL: http://192.168.0.50:11434
docker-compose up -d
# Accedi al terminale
docker exec -it maxclaw-agent python main.py
docker exec -it langcode-agent langcode
# Oppure esegui direttamente
docker run -it --rm maxclaw-agent
docker run -it --rm langcode-agent
```
### Utilizzo dell'API REST
Avvia il server API:
```bash
langcode server
# oppure
python api_server.py
```
@@ -261,6 +339,8 @@ L'API sarà disponibile su http://localhost:8000
#### Endpoint API Disponibili
- `GET /`: Interfaccia web
- `GET /monitoring`: Dashboard di monitoraggio
- `GET /marketplace`: Marketplace dei plugin
- `GET /health`: Health check
- `POST /chat`: Chat con l'agente
- `GET /configurations`: Elenca tutte le configurazioni
@@ -294,13 +374,295 @@ curl -X POST http://localhost:8000/configurations \
}'
```
## Analytics Avanzate
LangCode Agent include un sistema avanzato di analytics per monitorare l'uso e ottenere raccomandazioni.
### Report Analytics
Il report analytics include:
1. **Statistiche Conversazioni**:
- Numero totale di conversazioni
- Caratteri totali dei messaggi e risposte
- Lunghezza media di messaggi e risposte
2. **Utilizzo Provider**:
- Quale provider viene usato di più
- Distribuzione percentuale dell'uso
- Modello più utilizzato
3. **Metriche di Performance**:
- Tempo di risposta medio
- Tempo di risposta minimo e massimo
- Tempo totale di risposta
4. **Utilizzo Strumenti**:
- Quali strumenti vengono usati di più
- Numero di utilizzi per strumento
5. **Utilizzo Comandi**:
- Quali comandi vengono usati di più
- Numero di utilizzi per comando
6. **Pattern di Errori**:
- Errori ricorrenti
- Pattern di errore più comuni
7. **Statistiche Sessioni**:
- Numero totale di sessioni
- Durata media delle sessioni
- Messaggi medi per sessione
### Raccomandazioni
Il sistema genera automaticamente raccomandazioni basate sui pattern di utilizzo:
- **Diversificazione provider**: Se usi troppo un singolo provider
- **Ottimizzazione performance**: Se i tempi di risposta sono lenti
- **Utilizzo strumenti**: Se usi frequentemente certi strumenti
- **Gestione errori**: Se ci sono errori ricorrenti
- **Pattern di utilizzo**: Se l'uso è troppo basso o troppo alto
### Visualizzare le Analytics
```bash
# Usa il comando /analytics
/analytics
# Oppure esegui lo script direttamente
python analytics.py
```
### Configurazione
I dati di analytics vengono salvati in `~/.langcode/analytics/` e includono:
- Conversazioni (truncati per privacy)
- Utilizzo strumenti
- Utilizzo provider
- Utilizzo comandi
- Pattern di errori
- Sessioni utente
## Marketplace dei Plugin
LangCode Agent include un marketplace per scoprire e installare plugin.
### Accesso al Marketplace
```bash
# Avvia il server API
python api_server.py
# Accedi al marketplace
# http://localhost:8000/marketplace
```
### Funzionalità del Marketplace
Il marketplace include:
1. **Plugin Disponibili**:
- Catalogo di plugin disponibili per l'installazione
- Statistiche di download e valutazioni
- Descrizioni dettagliate
2. **Plugin Installati**:
- Elenco dei plugin attualmente installati
- Versioni e informazioni
- Opzioni di disinstallazione
3. **Ricerca Plugin**:
- Ricerca per nome o descrizione
- Filtraggio per categoria
- Ordinamento per popolarità
4. **Gestione Plugin**:
- Installazione con un click
- Disinstallazione semplice
- Aggiornamenti automatici
### Plugin Inclusi
1. **Example Plugin**: Plugin di esempio che dimostra come creare plugin
- Strumenti: `example_hello`, `example_echo`, `example_math`
- Comandi: `/example`, `/hello`, `/math`
2. **File Tools Plugin**: Strumenti aggiuntivi per la gestione dei file
- Strumenti: `file_find`, `file_size`, `file_copy`, `file_move`, `file_delete`, `file_info`, `dir_list`, `dir_create`
- Comandi: `/find`, `/size`, `/copy`, `/move`, `/delete`, `/info`, `/dir`, `/mkdir`
3. **System Utils Plugin**: Strumenti per informazioni di sistema
- Strumenti: `system_info`, `system_uptime`, `system_ip`, `system_hostname`, `system_platform`, `system_users`, `system_processes`, `system_disk_usage`
- Comandi: `/sysinfo`, `/uptime`, `/ip`, `/hostname`, `/platform`, `/users`, `/ps`, `/df`
### Installare Plugin dal Marketplace
```bash
# Usa il comando /plugins per vedere i plugin disponibili
/plugins
# Oppure usa l'interfaccia web
# http://localhost:8000/marketplace
```
### Creare un Plugin per il Marketplace
Per creare un plugin per il marketplace:
1. Crea un file Python seguendo la struttura dei plugin esistenti
2. Testa il plugin localmente
3. Sottometti il plugin al marketplace (futura implementazione)
## Dashboard di Monitoraggio
LangCode Agent include una dashboard di monitoraggio in tempo reale con supporto multilingue.
### Accesso alla Dashboard
```bash
# Avvia il server API
python api_server.py
# Accedi alla dashboard
# http://localhost:8000/monitoring
```
### Funzionalità della Dashboard
La dashboard include:
1. **Statistiche Generali**:
- Stato del sistema (online/offline)
- Uptime del sistema
- Richieste totali
- Errori totali
- Tasso di errore
2. **Provider Attivo**:
- Provider configurato
- Modello in uso
- Stato connessione
- Chiamate al provider
3. **Utilizzo Strumenti**:
- Strumenti caricati
- Plugin attivi
4. **Risorse di Sistema**:
- Utilizzo CPU
- Utilizzo memoria
- Utilizzo disco
5. **Performance**:
- Tempo di risposta medio
- Richieste al minuto
- Picco richieste
6. **Logs**:
- Logs recenti del sistema
- Filtraggio per tipo (info, warning, error, success)
7. **Informazioni di Sistema**:
- Hostname
- Piattaforma
- Architettura
- Versione Python
- Versione LangCode
### Supporto Multilingue della Dashboard
La dashboard supporta italiano e inglese:
- **Selettore lingua**: In alto a destra nella dashboard
- **Persistenza**: La preferenza di lingua viene salvata nel browser
- **Traduzione completa**: Tutti i testi della dashboard sono tradotti
- **Aggiornamento in tempo reale**: La lingua cambia istantaneamente
### Aggiornamento Automatico
La dashboard si aggiorna automaticamente ogni 30 secondi. Puoi anche aggiornare manualmente con il pulsante "🔄 Aggiorna Dati" (o "🔄 Refresh Data" in inglese).
## Interfaccia Web Responsive
L'interfaccia web è stata ottimizzata per essere responsive e funzionare su dispositivi mobili e desktop.
### Caratteristiche Responsive
1. **Layout adattivo**: Si adatta automaticamente a diverse dimensioni dello schermo
2. **Touch-friendly**: Pulsanti e campi ottimizzati per touch screen
3. **Navigazione mobile**: Menu e tab ottimizzati per dispositivi mobili
4. **Dimensioni appropriate**: Font e spaziature adatti a schermi piccoli
5. **Supporto multilingue**: Interfaccia in italiano e inglese
### Accesso da Dispositivi Mobili
L'interfaccia web è accessibile da qualsiasi dispositivo nella stessa rete:
```bash
# Avvia il server API
python api_server.py
# Accedi da un dispositivo mobile nella stessa rete
# http://<IP_DEL_SERVER>:8000
```
Esempio:
```bash
# Se il server è su 192.168.0.50
# http://192.168.0.50:8000
```
### Ottimizzazioni per Mobile
- **Pulsanti più grandi**: Facili da toccare su schermi touch
- **Layout a colonna singola**: Su schermi stretti
- **Font leggibili**: Dimensioni appropriate per la lettura su mobile
- **Navigazione semplificata**: Tab e menu ottimizzati per touch
- **Caricamento rapido**: Ottimizzazioni per connessioni lente
## Ottimizzazioni Performance
LangCode Agent include diverse ottimizzazioni per migliorare le performance:
### Caching
- **Configurazioni**: Le configurazioni vengono cachate per 30 secondi
- **Provider tools**: Gli strumenti provider vengono cachati per 5 minuti
- **Creazione agente**: L'agente viene cachato per 10 minuti
- **Risposte API**: Le risposte API vengono cachate per 10 secondi
### Connection Pooling
- **Pool di connessioni**: Gestisce le connessioni ai provider
- **Riutilizzo connessioni**: Riutilizza le connessioni esistenti
- **Pulizia automatica**: Rimuove le connessioni inattive dopo 5 minuti
### Lazy Loading
- **Caricamento moduli**: I moduli vengono caricati solo quando necessari
- **Caricamento strumenti**: Gli strumenti vengono caricati on-demand
- **Risorse pesanti**: Le risorse pesanti vengono caricate in modo lazy
### Batch Processing
- **Elaborazione parallela**: Le operazioni vengono eseguite in parallelo
- **Thread pool**: Utilizza un pool di thread per le operazioni concorrenti
- **Timeout configurabile**: Timeout configurabile per le operazioni batch
### Applicare le Ottimizzazioni
```bash
# Applica tutte le ottimizzazioni
python performance.py
# Visualizza il report delle performance
python performance.py
```
## Strumenti Unificati per Provider
MaxClaw Agent ora fornisce un'interfaccia unificata per gestire qualsiasi provider LLM. Una volta configurato un provider, puoi usare gli stessi comandi per tutti.
LangCode Agent ora fornisce un'interfaccia unificata per gestire qualsiasi provider LLM. Una volta configurato un provider, puoi usare gli stessi comandi per tutti.
### Provider Supportati
MaxClaw Agent supporta i seguenti provider:
LangCode Agent supporta i seguenti provider:
1. **Ollama** - Modelli locali
2. **OpenAI** - GPT-4o, GPT-4o-mini, GPT-4-turbo, ecc.
@@ -376,7 +738,7 @@ export GOOGLE_API_KEY="AIza..."
## Strumenti Personalizzati
MaxClaw Agent include anche strumenti personalizzati per gestire il tuo ambiente:
LangCode Agent include anche strumenti personalizzati per gestire il tuo ambiente:
### Strumenti Proxmox
Configura le variabili d'ambiente per abilitare gli strumenti Proxmox:
@@ -408,11 +770,21 @@ Gli strumenti Ollama specifici sono ancora disponibili per retrocompatibilità:
## Sistema di Plugin
MaxClaw Agent include un sistema di plugin per estendere le funzionalità.
LangCode Agent include un sistema di plugin per estendere le funzionalità.
### Plugin Inclusi
- **Example Plugin**: Plugin di esempio che dimostra come creare plugin
1. **Example Plugin**: Plugin di esempio che dimostra come creare plugin
- Strumenti: `example_hello`, `example_echo`, `example_math`
- Comandi: `/example`, `/hello`, `/math`
2. **File Tools Plugin**: Strumenti aggiuntivi per la gestione dei file
- Strumenti: `file_find`, `file_size`, `file_copy`, `file_move`, `file_delete`, `file_info`, `dir_list`, `dir_create`
- Comandi: `/find`, `/size`, `/copy`, `/move`, `/delete`, `/info`, `/dir`, `/mkdir`
3. **System Utils Plugin**: Strumenti per informazioni di sistema
- Strumenti: `system_info`, `system_uptime`, `system_ip`, `system_hostname`, `system_platform`, `system_users`, `system_processes`, `system_disk_usage`
- Comandi: `/sysinfo`, `/uptime`, `/ip`, `/hostname`, `/platform`, `/users`, `/ps`, `/df`
### Creare un Plugin
@@ -467,9 +839,72 @@ python -c "from plugins.manager import plugin_manager; plugin_manager.load_all_p
/mycommand
```
### Esempi di Utilizzo dei Plugin
#### File Tools Plugin
```bash
# Trova file per nome
"Find all .py files in the current directory"
# oppure
/find *.py
# Ottieni dimensione di un file
"Get the size of main.py"
# oppure
/size main.py
# Copia un file
"Copy config.json to backup_config.json"
# oppure
/copy config.json backup_config.json
# Elenca directory con dettagli
"List files in the current directory with details"
# oppure
/dir
```
#### System Utils Plugin
```bash
# Ottieni informazioni di sistema
"Show system information"
# oppure
/sysinfo
# Ottieni uptime del sistema
"How long has the system been running?"
# oppure
/uptime
# Ottieni indirizzo IP
"What is my IP address?"
# oppure
/ip
# Ottieni informazioni sulla piattaforma
"What platform am I running on?"
# oppure
/platform
# Elenca utenti loggati
"Who is logged in?"
# oppure
/users
# Elenca processi in esecuzione
"Show running processes"
# oppure
/ps
# Mostra utilizzo disco
"Show disk usage"
# oppure
/df
```
## Supporto Multilingue
MaxClaw Agent supporta più lingue per l'interfaccia utente.
LangCode Agent supporta più lingue per l'interfaccia utente.
### Lingue Disponibili
@@ -480,7 +915,7 @@ MaxClaw Agent supporta più lingue per l'interfaccia utente.
```bash
# Usa la variabile d'ambiente
export MAXCLAW_LANGUAGE=en
export LANGCODE_LANGUAGE=en
# Oppure usa il comando /language
/language en
@@ -495,6 +930,65 @@ Per aggiungere una nuova lingua:
3. Traduci tutti i valori
4. L'agente rileverà automaticamente la nuova lingua
### Dashboard Multilingue
La dashboard di monitoraggio supporta anche il cambio lingua:
- **Selettore lingua**: In alto a destra nella dashboard
- **Persistenza**: La preferenza viene salvata nel browser
- **Traduzione completa**: Tutti i testi sono tradotti in italiano e inglese
### Web UI Multilingue
L'interfaccia web supporta il cambio lingua:
- **Selettore lingua**: In alto a destra nell'interfaccia
- **Persistenza**: La preferenza viene salvata nel browser
- **Traduzione completa**: Tutti i testi sono tradotti in italiano e inglese
## Monitoraggio Performance
LangCode Agent include un sistema avanzato di monitoraggio delle performance.
### Dashboard Performance
Il dashboard raccoglie e analizza:
- Richieste API e tempi di risposta
- Utilizzo degli strumenti
- Utilizzo dei provider
- Statistiche di sistema (CPU, memoria, disco)
- Errori e tassi di successo
### Visualizzare le Performance
```bash
# Usa il comando /performance
/performance
# Oppure esegui lo script direttamente
python dashboard.py
```
### Report Disponibili
Il report include:
- Statistiche richieste (totale, errori, tasso di errore)
- Distribuzione codici di stato
- Endpoint più utilizzati
- Utilizzo degli strumenti (chiamate, tasso di successo, durata media)
- Utilizzo dei provider
- Statistiche di sistema
### Configurazione
I dati di performance vengono salvati in `~/.langcode/performance/` e includono:
- Richieste API
- Errori
- Tempi di risposta
- Utilizzo strumenti
- Utilizzo provider
- Statistiche di sistema
## Test
Esegui la suite di test:
@@ -514,7 +1008,7 @@ pytest tests/ -v
### Build dell'Immagine
```bash
docker build -t maxclaw-agent .
docker build -t langcode-agent .
```
### Esecuzione con Docker Compose
@@ -545,31 +1039,36 @@ docker run -it --rm \
-e PROXMOX_HOST=192.168.0.100 \
-e PROXMOX_USER=root \
-e PROXMOX_PASSWORD=password \
-e MAXCLAW_LANGUAGE=it \
maxclaw-agent
-e LANGCODE_LANGUAGE=it \
langcode-agent
```
## Personalizzazione
Per personalizzare l'agente, modifica `main.py`:
- Cambia il prompt di sistema predefinito in `create_maxclaw_agent`
- Cambia il prompt di sistema predefinito in `create_langcode_agent`
- Aggiungi strumenti personalizzati alla chiamata `create_deep_agent`
- Configura i sub-agenti o altre funzionalità di DeepAgents
- Crea plugin per estendere le funzionalità
## Monitoraggio e Logging
MaxClaw Agent include un sistema di monitoraggio e logging:
LangCode Agent include un sistema di monitoraggio e logging:
- **Log dettagliati**: Tutte le azioni vengono registrate in `~/.maxclaw/logs/`
- **Log dettagliati**: Tutte le azioni vengono registrate in `~/.langcode/logs/`
- **Statistiche di sistema**: Monitoraggio di uptime, richieste, errori
- **Metriche di performance**: Tempi di risposta, utilizzo cache
- **Report di stato**: Visualizzazione completa dello stato del sistema
- **Dashboard performance**: Monitoraggio avanzato delle performance
- **Dashboard web**: Monitoraggio in tempo reale via interfaccia web con supporto multilingue
- **Interfacce responsive**: Ottimizzate per dispositivi mobili e desktop
- **Marketplace plugin**: Scopri e installa plugin dal marketplace
- **Analytics avanzate**: Analisi dettagliate dell'uso e raccomandazioni
## Sicurezza
MaxClaw Agent implementa misure di sicurezza avanzate:
LangCode Agent implementa misure di sicurezza avanzate:
- **Crittografia a riposo**: Tutte le informazioni sensibili (chiavi API) sono crittografate usando la crittografia simmetrica Fernet
- **Permessi di file sicuri**: La directory di configurazione e i file sono impostati con accesso solo al proprietario (700 per le directory, 600 per i file)
Binary file not shown.
+494
View File
@@ -0,0 +1,494 @@
"""
Advanced analytics for LangCode Agent
Provides usage analytics, insights, and reporting
"""
import os
import sys
import json
import time
from datetime import datetime, timedelta
from typing import Dict, List, Any, Optional
from pathlib import Path
from collections import defaultdict, Counter
# Add current directory to path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
class AnalyticsEngine:
"""Advanced analytics engine for LangCode Agent"""
def __init__(self, data_dir: Optional[str] = None):
"""
Initialize analytics engine
Args:
data_dir: Directory for storing analytics data
"""
if data_dir is None:
self.data_dir = Path.home() / ".langcode" / "analytics"
else:
self.data_dir = Path(data_dir)
# Create data directory
self.data_dir.mkdir(parents=True, exist_ok=True)
# Analytics data
self.usage_data = {
"conversations": [],
"tool_usage": defaultdict(int),
"provider_usage": defaultdict(int),
"command_usage": defaultdict(int),
"error_patterns": defaultdict(int),
"response_times": [],
"user_sessions": []
}
# Load existing data
self._load_data()
def _load_data(self):
"""Load analytics data from file"""
data_file = self.data_dir / "analytics_data.json"
if data_file.exists():
try:
with open(data_file, 'r') as f:
data = json.load(f)
# Convert defaultdicts back
self.usage_data["tool_usage"] = defaultdict(int, data.get("tool_usage", {}))
self.usage_data["provider_usage"] = defaultdict(int, data.get("provider_usage", {}))
self.usage_data["command_usage"] = defaultdict(int, data.get("command_usage", {}))
self.usage_data["error_patterns"] = defaultdict(int, data.get("error_patterns", {}))
self.usage_data["conversations"] = data.get("conversations", [])
self.usage_data["response_times"] = data.get("response_times", [])
self.usage_data["user_sessions"] = data.get("user_sessions", [])
except Exception as e:
print(f"Warning: Could not load analytics data: {e}")
def _save_data(self):
"""Save analytics data to file"""
data_file = self.data_dir / "analytics_data.json"
try:
# Convert defaultdicts to regular dicts for JSON serialization
data_to_save = {
"conversations": self.usage_data["conversations"],
"tool_usage": dict(self.usage_data["tool_usage"]),
"provider_usage": dict(self.usage_data["provider_usage"]),
"command_usage": dict(self.usage_data["command_usage"]),
"error_patterns": dict(self.usage_data["error_patterns"]),
"response_times": self.usage_data["response_times"],
"user_sessions": self.usage_data["user_sessions"]
}
with open(data_file, 'w') as f:
json.dump(data_to_save, f, indent=2)
except Exception as e:
print(f"Warning: Could not save analytics data: {e}")
def record_conversation(self, user_message: str, agent_response: str,
provider: str, model: str, response_time: float):
"""Record a conversation for analytics"""
conversation = {
"timestamp": datetime.now().isoformat(),
"user_message": user_message[:100], # Truncate for privacy
"agent_response": agent_response[:100], # Truncate for privacy
"provider": provider,
"model": model,
"response_time": response_time,
"message_length": len(user_message),
"response_length": len(agent_response)
}
self.usage_data["conversations"].append(conversation)
self.usage_data["response_times"].append(response_time)
# Keep only last 1000 conversations
if len(self.usage_data["conversations"]) > 1000:
self.usage_data["conversations"] = self.usage_data["conversations"][-1000:]
# Keep only last 5000 response times
if len(self.usage_data["response_times"]) > 5000:
self.usage_data["response_times"] = self.usage_data["response_times"][-5000:]
self._save_data()
def record_tool_usage(self, tool_name: str, success: bool, duration: float):
"""Record tool usage"""
self.usage_data["tool_usage"][tool_name] += 1
self._save_data()
def record_provider_usage(self, provider: str, model: str):
"""Record provider usage"""
key = f"{provider}:{model}"
self.usage_data["provider_usage"][key] += 1
self._save_data()
def record_command_usage(self, command: str):
"""Record command usage"""
self.usage_data["command_usage"][command] += 1
self._save_data()
def record_error(self, error_type: str, error_message: str):
"""Record error patterns"""
# Simplify error message for pattern recognition
simplified = error_message[:50].lower()
self.usage_data["error_patterns"][simplified] += 1
self._save_data()
def record_user_session(self, session_id: str, duration: float,
messages_count: int, tools_used: List[str]):
"""Record user session"""
session = {
"session_id": session_id,
"timestamp": datetime.now().isoformat(),
"duration": duration,
"messages_count": messages_count,
"tools_used": tools_used,
"tools_count": len(tools_used)
}
self.usage_data["user_sessions"].append(session)
# Keep only last 100 sessions
if len(self.usage_data["user_sessions"]) > 100:
self.usage_data["user_sessions"] = self.usage_data["user_sessions"][-100:]
self._save_data()
def get_usage_summary(self, days: int = 30) -> Dict[str, Any]:
"""
Get usage summary for the last N days
Args:
days: Number of days to include
Returns:
Dictionary with usage summary
"""
cutoff_time = datetime.now() - timedelta(days=days)
# Filter recent conversations
recent_conversations = [
c for c in self.usage_data["conversations"]
if datetime.fromisoformat(c["timestamp"]) > cutoff_time
]
# Calculate statistics
total_conversations = len(recent_conversations)
total_messages = sum(c.get("message_length", 0) for c in recent_conversations)
total_responses = sum(c.get("response_length", 0) for c in recent_conversations)
# Provider usage
provider_counts = Counter()
for conv in recent_conversations:
provider = conv.get("provider", "unknown")
provider_counts[provider] += 1
# Model usage
model_counts = Counter()
for conv in recent_conversations:
model = conv.get("model", "unknown")
model_counts[model] += 1
# Response time statistics
recent_response_times = [
c.get("response_time", 0) for c in recent_conversations
if c.get("response_time")
]
if recent_response_times:
avg_response_time = sum(recent_response_times) / len(recent_response_times)
min_response_time = min(recent_response_times)
max_response_time = max(recent_response_times)
else:
avg_response_time = min_response_time = max_response_time = 0
# Tool usage
tool_usage = dict(self.usage_data["tool_usage"])
# Command usage
command_usage = dict(self.usage_data["command_usage"])
# Error patterns
error_patterns = dict(self.usage_data["error_patterns"])
# Session statistics
recent_sessions = [
s for s in self.usage_data["user_sessions"]
if datetime.fromisoformat(s["timestamp"]) > cutoff_time
]
total_sessions = len(recent_sessions)
total_session_duration = sum(s.get("duration", 0) for s in recent_sessions)
avg_session_duration = total_session_duration / total_sessions if total_sessions > 0 else 0
return {
"period_days": days,
"conversations": {
"total": total_conversations,
"total_messages_chars": total_messages,
"total_responses_chars": total_responses,
"avg_message_length": total_messages / total_conversations if total_conversations > 0 else 0,
"avg_response_length": total_responses / total_conversations if total_conversations > 0 else 0
},
"providers": {
"usage": dict(provider_counts),
"most_used": provider_counts.most_common(1)[0][0] if provider_counts else "None"
},
"models": {
"usage": dict(model_counts),
"most_used": model_counts.most_common(1)[0][0] if model_counts else "None"
},
"performance": {
"avg_response_time": avg_response_time,
"min_response_time": min_response_time,
"max_response_time": max_response_time,
"total_response_time": sum(recent_response_times)
},
"tools": {
"usage": tool_usage,
"most_used": max(tool_usage.items(), key=lambda x: x[1])[0] if tool_usage else "None"
},
"commands": {
"usage": command_usage,
"most_used": max(command_usage.items(), key=lambda x: x[1])[0] if command_usage else "None"
},
"errors": {
"patterns": error_patterns,
"most_common": max(error_patterns.items(), key=lambda x: x[1])[0] if error_patterns else "None"
},
"sessions": {
"total": total_sessions,
"total_duration": total_session_duration,
"avg_duration": avg_session_duration,
"avg_messages_per_session": sum(s.get("messages_count", 0) for s in recent_sessions) / total_sessions if total_sessions > 0 else 0
},
"generated_at": datetime.now().isoformat()
}
def get_detailed_report(self, days: int = 30) -> str:
"""
Get detailed analytics report
Args:
days: Number of days to include
Returns:
Formatted report string
"""
summary = self.get_usage_summary(days)
report = f"📊 Advanced Analytics Report (Last {days} days)\n"
report += "=" * 60 + "\n\n"
# Conversation statistics
report += "💬 Conversation Statistics:\n"
report += f" Total Conversations: {summary['conversations']['total']}\n"
report += f" Total Message Characters: {summary['conversations']['total_messages_chars']:,}\n"
report += f" Total Response Characters: {summary['conversations']['total_responses_chars']:,}\n"
report += f" Average Message Length: {summary['conversations']['avg_message_length']:.0f} chars\n"
report += f" Average Response Length: {summary['conversations']['avg_response_length']:.0f} chars\n\n"
# Provider usage
report += "🤖 Provider Usage:\n"
for provider, count in summary['providers']['usage'].items():
percentage = (count / summary['conversations']['total'] * 100) if summary['conversations']['total'] > 0 else 0
report += f" {provider}: {count} ({percentage:.1f}%)\n"
report += f" Most Used: {summary['providers']['most_used']}\n\n"
# Model usage
report += "🧠 Model Usage:\n"
for model, count in summary['models']['usage'].items():
percentage = (count / summary['conversations']['total'] * 100) if summary['conversations']['total'] > 0 else 0
report += f" {model}: {count} ({percentage:.1f}%)\n"
report += f" Most Used: {summary['models']['most_used']}\n\n"
# Performance metrics
report += "⚡ Performance Metrics:\n"
report += f" Average Response Time: {summary['performance']['avg_response_time']:.3f}s\n"
report += f" Minimum Response Time: {summary['performance']['min_response_time']:.3f}s\n"
report += f" Maximum Response Time: {summary['performance']['max_response_time']:.3f}s\n"
report += f" Total Response Time: {summary['performance']['total_response_time']:.1f}s\n\n"
# Tool usage
report += "🔧 Tool Usage:\n"
if summary['tools']['usage']:
for tool, count in sorted(summary['tools']['usage'].items(), key=lambda x: x[1], reverse=True)[:10]:
report += f" {tool}: {count}\n"
report += f" Most Used: {summary['tools']['most_used']}\n"
else:
report += " No tool usage recorded\n"
report += "\n"
# Command usage
report += "⌨️ Command Usage:\n"
if summary['commands']['usage']:
for command, count in sorted(summary['commands']['usage'].items(), key=lambda x: x[1], reverse=True)[:10]:
report += f" {command}: {count}\n"
report += f" Most Used: {summary['commands']['most_used']}\n"
else:
report += " No command usage recorded\n"
report += "\n"
# Error patterns
report += "❌ Error Patterns:\n"
if summary['errors']['patterns']:
for pattern, count in sorted(summary['errors']['patterns'].items(), key=lambda x: x[1], reverse=True)[:5]:
report += f" {pattern}: {count}\n"
report += f" Most Common: {summary['errors']['most_common']}\n"
else:
report += " No errors recorded\n"
report += "\n"
# Session statistics
report += "👥 Session Statistics:\n"
report += f" Total Sessions: {summary['sessions']['total']}\n"
report += f" Total Session Duration: {summary['sessions']['total_duration']:.1f}s\n"
report += f" Average Session Duration: {summary['sessions']['avg_duration']:.1f}s\n"
report += f" Average Messages per Session: {summary['sessions']['avg_messages_per_session']:.1f}\n"
return report
def get_insights(self, days: int = 30) -> List[str]:
"""
Get insights and recommendations based on usage patterns
Args:
days: Number of days to analyze
Returns:
List of insights and recommendations
"""
summary = self.get_usage_summary(days)
insights = []
# Provider insights
if summary['providers']['usage']:
most_used_provider = summary['providers']['most_used']
provider_count = summary['providers']['usage'].get(most_used_provider, 0)
total_conversations = summary['conversations']['total']
if total_conversations > 0:
provider_percentage = (provider_count / total_conversations) * 100
if provider_percentage > 80:
insights.append(f"💡 You're heavily using {most_used_provider} ({provider_percentage:.0f}% of conversations). Consider diversifying with other providers for different use cases.")
elif provider_percentage < 20:
insights.append(f"💡 You're barely using {most_used_provider} ({provider_percentage:.0f}% of conversations). Consider if this provider is meeting your needs.")
# Performance insights
avg_response_time = summary['performance']['avg_response_time']
if avg_response_time > 5.0:
insights.append(f"⚠️ Average response time is high ({avg_response_time:.1f}s). Consider using faster models or optimizing your queries.")
elif avg_response_time < 1.0:
insights.append(f"✅ Great response times! Average is {avg_response_time:.1f}s.")
# Tool usage insights
if summary['tools']['usage']:
most_used_tool = summary['tools']['most_used']
tool_count = summary['tools']['usage'].get(most_used_tool, 0)
insights.append(f"🔧 You frequently use the '{most_used_tool}' tool ({tool_count} times). Consider creating shortcuts or aliases for it.")
# Error insights
if summary['errors']['patterns']:
most_common_error = summary['errors']['most_common']
error_count = summary['errors']['patterns'].get(most_common_error, 0)
if error_count > 5:
insights.append(f"❌ You're encountering a recurring error: '{most_common_error}' ({error_count} times). Consider investigating the root cause.")
# Session insights
avg_session_duration = summary['sessions']['avg_duration']
if avg_session_duration > 3600: # More than 1 hour
insights.append(f"⏱️ Your average session is long ({avg_session_duration/60:.0f} minutes). Consider breaking work into shorter sessions for better focus.")
# Usage pattern insights
total_conversations = summary['conversations']['total']
if total_conversations > 100:
insights.append(f"📈 You're a power user with {total_conversations} conversations in {days} days!")
elif total_conversations < 10:
insights.append(f"💡 You've only had {total_conversations} conversations in {days} days. Consider exploring more features.")
# Default insight if none generated
if not insights:
insights.append("📊 Your usage patterns look normal. Keep using LangCode Agent!")
return insights
def clear_old_data(self, days: int = 90):
"""
Clear data older than specified days
Args:
days: Number of days to keep data
"""
cutoff_time = datetime.now() - timedelta(days=days)
# Filter out old conversations
self.usage_data["conversations"] = [
c for c in self.usage_data["conversations"]
if datetime.fromisoformat(c["timestamp"]) > cutoff_time
]
# Filter out old sessions
self.usage_data["user_sessions"] = [
s for s in self.usage_data["user_sessions"]
if datetime.fromisoformat(s["timestamp"]) > cutoff_time
]
# Save cleaned data
self._save_data()
print(f"✅ Cleared analytics data older than {days} days")
# Global analytics engine instance
analytics_engine = AnalyticsEngine()
def get_usage_summary(days: int = 30) -> Dict[str, Any]:
"""
Get usage summary
Args:
days: Number of days to include
Returns:
Usage summary dictionary
"""
return analytics_engine.get_usage_summary(days)
def get_analytics_report(days: int = 30) -> str:
"""
Get formatted analytics report
Args:
days: Number of days to include
Returns:
Formatted report string
"""
return analytics_engine.get_detailed_report(days)
def get_analytics_insights(days: int = 30) -> List[str]:
"""
Get analytics insights and recommendations
Args:
days: Number of days to analyze
Returns:
List of insights and recommendations
"""
return analytics_engine.get_insights(days)
if __name__ == "__main__":
# Print analytics report when run directly
print(get_analytics_report())
print("\n" + "=" * 60)
print("💡 Insights and Recommendations:")
for insight in get_analytics_insights():
print(f" {insight}")
+38 -11
View File
@@ -1,6 +1,6 @@
"""
MaxClaw Agent API Server
Provides REST API for remote access to MaxClaw Agent
LangCode Agent API Server
Provides REST API for remote access to LangCode Agent
"""
import os
import sys
@@ -16,13 +16,13 @@ import uvicorn
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from config_manager import config_manager
from main import create_maxclaw_agent
from main import create_langcode_agent
from langchain_core.messages import HumanMessage
# Initialize FastAPI app
app = FastAPI(
title="MaxClaw Agent API",
description="REST API for MaxClaw Agent - configurable AI agent",
title="LangCode Agent API",
description="REST API for LangCode Agent - configurable AI agent",
version="1.0.0"
)
@@ -83,7 +83,7 @@ def get_agent():
global agent
if agent is None:
try:
agent = create_maxclaw_agent()
agent = create_langcode_agent()
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to create agent: {str(e)}")
return agent
@@ -94,8 +94,8 @@ async def startup_event():
"""Initialize agent on startup"""
global agent
try:
agent = create_maxclaw_agent()
print("✅ MaxClaw Agent API server started")
agent = create_langcode_agent()
print("✅ LangCode Agent API server started")
except Exception as e:
print(f"❌ Failed to initialize agent: {e}")
@@ -108,17 +108,32 @@ async def root():
return FileResponse(web_index)
else:
return {
"message": "MaxClaw Agent API",
"message": "LangCode Agent API",
"version": "1.0.0",
"endpoints": {
"chat": "/chat",
"configurations": "/configurations",
"health": "/health",
"web_ui": "/static/index.html"
"web_ui": "/static/index.html",
"monitoring": "/monitoring"
}
}
@app.get("/monitoring")
async def monitoring():
"""Monitoring dashboard endpoint"""
monitoring_file = os.path.join(web_dir, "monitoring.html")
if os.path.exists(monitoring_file):
return FileResponse(monitoring_file)
else:
return {
"message": "Monitoring dashboard not available",
"version": "1.0.0",
"note": "Monitoring dashboard file not found"
}
@app.get("/health")
async def health_check():
"""Health check endpoint"""
@@ -127,7 +142,7 @@ async def health_check():
@app.post("/chat", response_model=ChatResponse)
async def chat(request: ChatRequest):
"""Chat with MaxClaw Agent"""
"""Chat with LangCode Agent"""
try:
agent_instance = get_agent()
@@ -283,6 +298,18 @@ async def list_providers():
"description": "OpenRouter multi-provider access",
"required_env": ["OPENROUTER_API_KEY"],
"optional_env": ["OPENROUTER_BASE_URL"]
},
{
"name": "anthropic",
"description": "Anthropic Claude models",
"required_env": ["ANTHROPIC_API_KEY"],
"optional_env": []
},
{
"name": "google",
"description": "Google Gemini models",
"required_env": ["GOOGLE_API_KEY"],
"optional_env": []
}
]
}
File renamed without changes.
File renamed without changes.
+3 -3
View File
@@ -1,5 +1,5 @@
"""
Command-line interface for managing MaxClaw Agent configurations
Command-line interface for managing LangCode Agent configurations
Enhanced with export/import, search, and better formatting
"""
import sys
@@ -11,7 +11,7 @@ from typing import Optional, Dict, Any
from config_manager import config_manager
def print_configurations(configs=None, title="MaxClaw Agent Configurations"):
def print_configurations(configs=None, title="LangCode Agent Configurations"):
"""Print all configurations in a formatted way"""
if configs is None:
configs = config_manager.get_configurations()
@@ -405,7 +405,7 @@ def import_configurations():
def main():
"""Main CLI entry point"""
parser = argparse.ArgumentParser(description="MaxClaw Agent Configuration Manager")
parser = argparse.ArgumentParser(description="LangCode Agent Configuration Manager")
subparsers = parser.add_subparsers(dest='command', help='Available commands')
# List command
+4 -4
View File
@@ -1,5 +1,5 @@
"""
Configuration manager for MaxClaw Agent
Configuration manager for LangCode Agent
Handles persistent storage of provider configurations with encryption and validation
"""
import json
@@ -17,17 +17,17 @@ except ImportError:
class ConfigManager:
"""Manages MaxClaw Agent configuration with encryption and validation"""
"""Manages LangCode Agent configuration with encryption and validation"""
def __init__(self, config_dir: Optional[str] = None):
"""
Initialize the config manager
Args:
config_dir: Directory to store config files. If None, uses ~/.maxclaw
config_dir: Directory to store config files. If None, uses ~/.langcode
"""
if config_dir is None:
self.config_dir = Path.home() / ".maxclaw"
self.config_dir = Path.home() / ".langcode"
else:
self.config_dir = Path(config_dir)
+2 -2
View File
@@ -1,5 +1,5 @@
"""
Text User Interface for MaxClaw Agent configuration management
Text User Interface for LangCode Agent configuration management
Enhanced with search/filter, export/import, and connection testing
"""
import curses
@@ -11,7 +11,7 @@ from typing import Optional, List, Dict, Any
from config_manager import config_manager
def draw_menu(stdscr, selected_row_idx, options, title="MaxClaw Agent Configuration TUI"):
def draw_menu(stdscr, selected_row_idx, options, title="LangCode Agent Configuration TUI"):
"""Draw a menu with options"""
stdscr.clear()
h, w = stdscr.getmaxyx()
+420
View File
@@ -0,0 +1,420 @@
"""
Performance monitoring dashboard for LangCode Agent
"""
import os
import sys
import time
import json
from datetime import datetime, timedelta
from typing import Dict, List, Any, Optional
from pathlib import Path
import threading
# Add current directory to path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
class PerformanceDashboard:
"""Performance monitoring dashboard"""
def __init__(self, data_dir: Optional[str] = None):
"""
Initialize performance dashboard
Args:
data_dir: Directory for storing performance data
"""
if data_dir is None:
self.data_dir = Path.home() / ".langcode" / "performance"
else:
self.data_dir = Path(data_dir)
# Create data directory
self.data_dir.mkdir(parents=True, exist_ok=True)
# Performance metrics
self.metrics = {
"requests": [],
"errors": [],
"response_times": [],
"tool_usage": {},
"provider_usage": {},
"system_stats": []
}
# Load existing data
self._load_data()
# Start background monitoring
self._start_monitoring()
def _load_data(self):
"""Load existing performance data"""
data_file = self.data_dir / "performance_data.json"
if data_file.exists():
try:
with open(data_file, 'r') as f:
data = json.load(f)
self.metrics.update(data)
except Exception as e:
print(f"Warning: Could not load performance data: {e}")
def _save_data(self):
"""Save performance data to file"""
data_file = self.data_dir / "performance_data.json"
try:
with open(data_file, 'w') as f:
json.dump(self.metrics, f, indent=2)
except Exception as e:
print(f"Warning: Could not save performance data: {e}")
def _start_monitoring(self):
"""Start background monitoring thread"""
def monitor():
while True:
time.sleep(60) # Collect data every minute
self._collect_system_stats()
self._save_data()
thread = threading.Thread(target=monitor, daemon=True)
thread.start()
def _collect_system_stats(self):
"""Collect system statistics"""
try:
import psutil
stats = {
"timestamp": datetime.now().isoformat(),
"cpu_percent": psutil.cpu_percent(interval=1),
"memory_percent": psutil.virtual_memory().percent,
"disk_usage": psutil.disk_usage('/').percent,
"network_connections": len(psutil.net_connections())
}
self.metrics["system_stats"].append(stats)
# Keep only last 1000 entries
if len(self.metrics["system_stats"]) > 1000:
self.metrics["system_stats"] = self.metrics["system_stats"][-1000:]
except ImportError:
# psutil not available, skip system stats
pass
except Exception as e:
print(f"Warning: Could not collect system stats: {e}")
def record_request(self, endpoint: str, method: str, status_code: int, duration: float):
"""Record an API request"""
request = {
"timestamp": datetime.now().isoformat(),
"endpoint": endpoint,
"method": method,
"status_code": status_code,
"duration": duration
}
self.metrics["requests"].append(request)
# Keep only last 10000 requests
if len(self.metrics["requests"]) > 10000:
self.metrics["requests"] = self.metrics["requests"][-10000:]
def record_error(self, error_type: str, error_message: str, context: Optional[str] = None):
"""Record an error"""
error = {
"timestamp": datetime.now().isoformat(),
"type": error_type,
"message": error_message,
"context": context
}
self.metrics["errors"].append(error)
# Keep only last 1000 errors
if len(self.metrics["errors"]) > 1000:
self.metrics["errors"] = self.metrics["errors"][-1000:]
def record_response_time(self, operation: str, duration: float):
"""Record response time for an operation"""
response_time = {
"timestamp": datetime.now().isoformat(),
"operation": operation,
"duration": duration
}
self.metrics["response_times"].append(response_time)
# Keep only last 5000 response times
if len(self.metrics["response_times"]) > 5000:
self.metrics["response_times"] = self.metrics["response_times"][-5000:]
def record_tool_usage(self, tool_name: str, success: bool, duration: float):
"""Record tool usage"""
if tool_name not in self.metrics["tool_usage"]:
self.metrics["tool_usage"][tool_name] = {
"total_calls": 0,
"successful_calls": 0,
"failed_calls": 0,
"total_duration": 0,
"avg_duration": 0
}
tool_stats = self.metrics["tool_usage"][tool_name]
tool_stats["total_calls"] += 1
tool_stats["total_duration"] += duration
if success:
tool_stats["successful_calls"] += 1
else:
tool_stats["failed_calls"] += 1
tool_stats["avg_duration"] = tool_stats["total_duration"] / tool_stats["total_calls"]
def record_provider_usage(self, provider: str, model: str, success: bool):
"""Record provider usage"""
key = f"{provider}:{model}"
if key not in self.metrics["provider_usage"]:
self.metrics["provider_usage"][key] = {
"total_calls": 0,
"successful_calls": 0,
"failed_calls": 0
}
provider_stats = self.metrics["provider_usage"][key]
provider_stats["total_calls"] += 1
if success:
provider_stats["successful_calls"] += 1
else:
provider_stats["failed_calls"] += 1
def get_summary(self, hours: int = 24) -> Dict[str, Any]:
"""
Get performance summary for the last N hours
Args:
hours: Number of hours to include in summary
Returns:
Dictionary with performance summary
"""
cutoff_time = datetime.now() - timedelta(hours=hours)
# Filter recent data
recent_requests = [
r for r in self.metrics["requests"]
if datetime.fromisoformat(r["timestamp"]) > cutoff_time
]
recent_errors = [
e for e in self.metrics["errors"]
if datetime.fromisoformat(e["timestamp"]) > cutoff_time
]
recent_response_times = [
rt for rt in self.metrics["response_times"]
if datetime.fromisoformat(rt["timestamp"]) > cutoff_time
]
# Calculate statistics
total_requests = len(recent_requests)
total_errors = len(recent_errors)
if total_requests > 0:
error_rate = total_errors / total_requests
avg_response_time = sum(r["duration"] for r in recent_requests) / total_requests
else:
error_rate = 0
avg_response_time = 0
# Status code distribution
status_codes = {}
for request in recent_requests:
code = request["status_code"]
status_codes[code] = status_codes.get(code, 0) + 1
# Endpoint usage
endpoints = {}
for request in recent_requests:
endpoint = request["endpoint"]
endpoints[endpoint] = endpoints.get(endpoint, 0) + 1
# Tool usage summary
tool_summary = {}
for tool_name, stats in self.metrics["tool_usage"].items():
tool_summary[tool_name] = {
"total_calls": stats["total_calls"],
"success_rate": stats["successful_calls"] / stats["total_calls"] if stats["total_calls"] > 0 else 0,
"avg_duration": stats["avg_duration"]
}
# Provider usage summary
provider_summary = {}
for provider_key, stats in self.metrics["provider_usage"].items():
provider_summary[provider_key] = {
"total_calls": stats["total_calls"],
"success_rate": stats["successful_calls"] / stats["total_calls"] if stats["total_calls"] > 0 else 0
}
# System stats (if available)
system_stats = {}
if self.metrics["system_stats"]:
latest_stats = self.metrics["system_stats"][-1]
system_stats = {
"cpu_percent": latest_stats.get("cpu_percent", 0),
"memory_percent": latest_stats.get("memory_percent", 0),
"disk_usage": latest_stats.get("disk_usage", 0)
}
return {
"period_hours": hours,
"total_requests": total_requests,
"total_errors": total_errors,
"error_rate": error_rate,
"avg_response_time": avg_response_time,
"status_codes": status_codes,
"endpoints": endpoints,
"tool_usage": tool_summary,
"provider_usage": provider_summary,
"system_stats": system_stats,
"generated_at": datetime.now().isoformat()
}
def get_detailed_report(self, hours: int = 24) -> str:
"""
Get detailed performance report
Args:
hours: Number of hours to include in report
Returns:
Formatted report string
"""
summary = self.get_summary(hours)
report = f"📊 Performance Report (Last {hours} hours)\n"
report += "=" * 50 + "\n\n"
# Request statistics
report += f"📈 Request Statistics:\n"
report += f" Total Requests: {summary['total_requests']}\n"
report += f" Total Errors: {summary['total_errors']}\n"
report += f" Error Rate: {summary['error_rate']:.2%}\n"
report += f" Avg Response Time: {summary['avg_response_time']:.3f}s\n\n"
# Status codes
if summary['status_codes']:
report += f"📋 Status Code Distribution:\n"
for code, count in sorted(summary['status_codes'].items()):
report += f" {code}: {count}\n"
report += "\n"
# Top endpoints
if summary['endpoints']:
report += f"🔗 Top Endpoints:\n"
sorted_endpoints = sorted(summary['endpoints'].items(), key=lambda x: x[1], reverse=True)[:10]
for endpoint, count in sorted_endpoints:
report += f" {endpoint}: {count}\n"
report += "\n"
# Tool usage
if summary['tool_usage']:
report += f"🔧 Tool Usage:\n"
for tool_name, stats in summary['tool_usage'].items():
report += f" {tool_name}:\n"
report += f" Calls: {stats['total_calls']}\n"
report += f" Success Rate: {stats['success_rate']:.2%}\n"
report += f" Avg Duration: {stats['avg_duration']:.3f}s\n"
report += "\n"
# Provider usage
if summary['provider_usage']:
report += f"🤖 Provider Usage:\n"
for provider_key, stats in summary['provider_usage'].items():
report += f" {provider_key}:\n"
report += f" Calls: {stats['total_calls']}\n"
report += f" Success Rate: {stats['success_rate']:.2%}\n"
report += "\n"
# System stats
if summary['system_stats']:
report += f"💻 System Statistics:\n"
report += f" CPU Usage: {summary['system_stats']['cpu_percent']}%\n"
report += f" Memory Usage: {summary['system_stats']['memory_percent']}%\n"
report += f" Disk Usage: {summary['system_stats']['disk_usage']}%\n"
return report
def clear_old_data(self, days: int = 30):
"""
Clear data older than specified days
Args:
days: Number of days to keep data
"""
cutoff_time = datetime.now() - timedelta(days=days)
# Filter out old data
self.metrics["requests"] = [
r for r in self.metrics["requests"]
if datetime.fromisoformat(r["timestamp"]) > cutoff_time
]
self.metrics["errors"] = [
e for e in self.metrics["errors"]
if datetime.fromisoformat(e["timestamp"]) > cutoff_time
]
self.metrics["response_times"] = [
rt for rt in self.metrics["response_times"]
if datetime.fromisoformat(rt["timestamp"]) > cutoff_time
]
self.metrics["system_stats"] = [
s for s in self.metrics["system_stats"]
if datetime.fromisoformat(s["timestamp"]) > cutoff_time
]
# Save cleaned data
self._save_data()
print(f"✅ Cleared data older than {days} days")
# Global performance dashboard instance
performance_dashboard = PerformanceDashboard()
def get_performance_summary(hours: int = 24) -> Dict[str, Any]:
"""
Get performance summary
Args:
hours: Number of hours to include
Returns:
Performance summary dictionary
"""
return performance_dashboard.get_summary(hours)
def get_performance_report(hours: int = 24) -> str:
"""
Get formatted performance report
Args:
hours: Number of hours to include
Returns:
Formatted report string
"""
return performance_dashboard.get_detailed_report(hours)
if __name__ == "__main__":
# Print performance report when run directly
print(get_performance_report())
+7 -7
View File
@@ -1,13 +1,13 @@
version: '3.8'
services:
maxclaw:
langcode:
build: .
container_name: maxclaw-agent
container_name: langcode-agent
restart: unless-stopped
volumes:
# Mount configuration directory for persistence
- maxclaw-config:/root/.maxclaw
- langcode-config:/root/.langcode
# Mount current directory for development (optional)
# - .:/app
environment:
@@ -25,7 +25,7 @@ services:
- PROXMOX_PASSWORD=${PROXMOX_PASSWORD:-}
- PROXMOX_TOKEN=${PROXMOX_TOKEN:-}
networks:
- maxclaw-network
- langcode-network
# Interactive mode for terminal usage
stdin_open: true
tty: true
@@ -40,14 +40,14 @@ services:
# ports:
# - "11434:11434"
# networks:
# - maxclaw-network
# - langcode-network
volumes:
maxclaw-config:
langcode-config:
driver: local
# ollama-data:
# driver: local
networks:
maxclaw-network:
langcode-network:
driver: bridge
+10 -10
View File
@@ -1,8 +1,8 @@
# MaxClaw Agent: Architettura e Guida all'Utilizzo
# LangCode Agent: Architettura e Guida all'Utilizzo
## Cos'è MaxClaw Agent?
## Cos'è LangCode Agent?
MaxClaw Agent è un **agente AI intelligente** che può essere configurato per lavorare con diversi modelli di intelligenza artificiale (come GPT-4o di OpenAI, modelli locali con Ollama, o Claude tramite OpenRouter). In pratica, è come avere un assistente virtuale programmabile che può:
LangCode Agent è un **agente AI intelligente** che può essere configurato per lavorare con diversi modelli di intelligenza artificiale (come GPT-4o di OpenAI, modelli locali con Ollama, o Claude tramite OpenRouter). In pratica, è come avere un assistente virtuale programmabile che può:
- **Leggere e scrivere file** sul tuo computer
- **Eseguire comandi** di sistema
@@ -16,7 +16,7 @@ MaxClaw Agent è un **agente AI intelligente** che può essere configurato per l
```
┌─────────────────────────────────────────────────────────────┐
│ MaxClaw Agent │
│ LangCode Agent │
├─────────────────────────────────────────────────────────────┤
│ 1. Agente Principale (main.py) │
│ - Coordina tutto il sistema │
@@ -327,13 +327,13 @@ python main.py
### 2. Prima Conversazione
```
👤 Tu: Ciao, chi sei?
🤖 MaxClaw: Sono MaxClaw, un agente AI configurabile. Posso aiutarti a gestire file, eseguire comandi, analizzare dati e molto altro. Come posso aiutarti oggi?
🤖 LangCode: Sono LangCode, un agente AI configurabile. Posso aiutarti a gestire file, eseguire comandi, analizzare dati e molto altro. Come posso aiutarti oggi?
👤 Tu: Mostrami i file nella directory corrente
🤖 MaxClaw: [Esegue ls e mostra i file]
🤖 LangCode: [Esegue ls e mostra i file]
👤 Tu: Crea un file di testo con "Hello World"
🤖 MaxClaw: [Crea hello.txt con il contenuto specificato]
🤖 LangCode: [Crea hello.txt con il contenuto specificato]
```
### 3. Configurazione Avanzata
@@ -391,8 +391,8 @@ python config_cli.py test "openai-config"
### Problema: "Permessi insufficienti"
**Soluzione**: Verifica i permessi dei file
```bash
ls -la ~/.maxclaw/
chmod 600 ~/.maxclaw/config.json
ls -la ~/.langcode/
chmod 600 ~/.langcode/config.json
```
## Prossimi Passi
@@ -404,6 +404,6 @@ chmod 600 ~/.maxclaw/config.json
## Conclusione
MaxClaw Agent è uno strumento potente e flessibile che può adattarsi a molteplici esigenze. Che tu voglia gestire file, analizzare dati, amministrare server o sperimentare con modelli AI, questo agente può aiutarti a automatizzare e semplificare il tuo lavoro.
LangCode Agent è uno strumento potente e flessibile che può adattarsi a molteplici esigenze. Che tu voglia gestire file, analizzare dati, amministrare server o sperimentare con modelli AI, questo agente può aiutarti a automatizzare e semplificare il tuo lavoro.
La chiave è iniziare con task semplici e gradualmente esplorare funzionalità più avanzate man mano che acquisisci familiarità con il sistema.
+4 -4
View File
@@ -7,7 +7,7 @@ import os
import sys
sys.path.insert(0, '.')
from main import create_maxclaw_agent
from main import create_langcode_agent
def example_with_openai():
"""Example using OpenAI"""
@@ -18,7 +18,7 @@ def example_with_openai():
# os.environ["OPENAI_API_KEY"] = "your-api-key"
try:
agent = create_maxclaw_agent()
agent = create_langcode_agent()
print("✅ OpenAI agent created")
# Uncomment to test (requires API key)
# result = agent.invoke({"messages": [{"role": "user", "content": "Say hello in Italian"}]})
@@ -35,7 +35,7 @@ def example_with_ollama():
os.environ["OLLAMA_BASE_URL"] = "https://ollama.solarpi.net" # From user's profile
try:
agent = create_maxclaw_agent()
agent = create_langcode_agent()
print("✅ Ollama agent created")
# Uncomment to test (requires Ollama server)
# result = agent.invoke({"messages": [{"role": "user", "content": "Say hello in Italian"}]})
@@ -53,7 +53,7 @@ def example_with_openrouter():
# os.environ["OPENROUTER_API_KEY"] = "your-api-key"
try:
agent = create_maxclaw_agent()
agent = create_langcode_agent()
print("✅ OpenRouter agent created")
# Uncomment to test (requires API key)
# result = agent.invoke({"messages": [{"role": "user", "content": "Say hello in Italian"}]})
+3 -3
View File
@@ -1,5 +1,5 @@
"""
Internationalization (i18n) support for MaxClaw Agent
Internationalization (i18n) support for LangCode Agent
"""
import os
import json
@@ -8,7 +8,7 @@ from pathlib import Path
class I18n:
"""Internationalization manager for MaxClaw Agent"""
"""Internationalization manager for LangCode Agent"""
def __init__(self, default_language: str = "en"):
"""
@@ -31,7 +31,7 @@ class I18n:
self._load_language(self.default_language)
# Try to load current language from environment
env_lang = os.getenv("MAXCLAW_LANGUAGE")
env_lang = os.getenv("LANGCODE_LANGUAGE")
if env_lang:
self.set_language(env_lang)
+2 -2
View File
@@ -1,5 +1,5 @@
{
"app.name": "MaxClaw Agent",
"app.name": "LangCode Agent",
"app.description": "A configurable agent harness using DeepAgents",
"commands.help": "Show available commands",
@@ -52,7 +52,7 @@
"plugins.disabled": "Disabled",
"plugins.no_plugins": "No plugins found.",
"agent.initializing": "Initializing MaxClaw Agent...",
"agent.initializing": "Initializing LangCode Agent...",
"agent.initialized": "Agent initialized successfully!",
"agent.tools.available": "Available tools:",
"agent.tools.provider": "Provider tools: list models, test connections, set defaults",
+2 -2
View File
@@ -1,5 +1,5 @@
{
"app.name": "MaxClaw Agent",
"app.name": "LangCode Agent",
"app.description": "Un agente configurabile basato su DeepAgents",
"commands.help": "Mostra i comandi disponibili",
@@ -52,7 +52,7 @@
"plugins.disabled": "Disabilitato",
"plugins.no_plugins": "Nessun plugin trovato.",
"agent.initializing": "Inizializzazione MaxClaw Agent...",
"agent.initializing": "Inizializzazione LangCode Agent...",
"agent.initialized": "Agente inizializzato con successo!",
"agent.tools.available": "Strumenti disponibili:",
"agent.tools.provider": "Strumenti provider: elenca modelli, testa connessioni, imposta predefiniti",
Executable
+551
View File
@@ -0,0 +1,551 @@
#!/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()
+8 -8
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""
MaxClaw Agent - A configurable agent harness using DeepAgents
LangCode Agent - A configurable agent harness using DeepAgents
Supports multiple LLM providers via LangChain's init_chat_model
Enhanced with unified provider tools and custom tools
"""
@@ -74,7 +74,7 @@ def load_model_from_config():
def get_custom_tools() -> List[Tool]:
"""
Get custom tools for the MaxClaw agent
Get custom tools for the LangCode agent
Returns:
List of custom tools
@@ -114,12 +114,12 @@ def get_custom_tools() -> List[Tool]:
return custom_tools
def create_maxclaw_agent(system_prompt: Optional[str] = None):
def create_langcode_agent(system_prompt: Optional[str] = None):
"""
Create and return a DeepAgent instance with configurable model.
"""
if system_prompt is None:
system_prompt = """You are MaxClaw, a helpful AI agent that can plan, use tools,
system_prompt = """You are LangCode, a helpful AI agent that can plan, use tools,
manage context, and delegate to subagents. You are designed to be configurable
to work with various LLM providers.
@@ -371,12 +371,12 @@ def process_command(user_input: str) -> bool:
def main():
"""
Main entry point for the MaxClaw agent.
Main entry point for the LangCode agent.
"""
print("🚀 Initializing MaxClaw Agent...")
print("🚀 Initializing LangCode Agent...")
try:
agent = create_maxclaw_agent()
agent = create_langcode_agent()
print("✅ Agent initialized successfully!")
# Show available tools
@@ -408,7 +408,7 @@ def main():
result = agent.invoke({"messages": [HumanMessage(content=user_input)]})
# Extract the AI's response
ai_message = result["messages"][-1]
print(f"\n🤖 MaxClaw: {ai_message.content}")
print(f"\n🤖 LangCode: {ai_message.content}")
except Exception as e:
print(f"\n❌ Error: {str(e)}")
+11
View File
@@ -0,0 +1,11 @@
"""
LangCode Agent Marketplace Package
Provides plugin marketplace functionality
"""
from .manager import MarketplaceManager, marketplace_manager
__all__ = [
'MarketplaceManager',
'marketplace_manager'
]
+275
View File
@@ -0,0 +1,275 @@
"""
Plugin marketplace manager for LangCode Agent
"""
import os
import sys
import json
import requests
from typing import Dict, List, Any, Optional
from pathlib import Path
# Add parent directory to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
class MarketplaceManager:
"""Manages plugin marketplace for LangCode Agent"""
def __init__(self, marketplace_url: str = "https://api.github.com/repos/langcode-agent/plugins/contents"):
"""
Initialize marketplace manager
Args:
marketplace_url: URL to the plugin marketplace
"""
self.marketplace_url = marketplace_url
self.local_plugins_dir = Path.home() / ".langcode" / "plugins"
self.marketplace_cache_file = Path.home() / ".langcode" / "marketplace_cache.json"
# Ensure directories exist
self.local_plugins_dir.mkdir(parents=True, exist_ok=True)
# Load cache
self.cache = self._load_cache()
def _load_cache(self) -> Dict[str, Any]:
"""Load marketplace cache"""
if self.marketplace_cache_file.exists():
try:
with open(self.marketplace_cache_file, 'r') as f:
return json.load(f)
except Exception:
pass
return {"plugins": {}, "last_updated": None}
def _save_cache(self):
"""Save marketplace cache"""
try:
with open(self.marketplace_cache_file, 'w') as f:
json.dump(self.cache, f, indent=2)
except Exception as e:
print(f"Warning: Could not save marketplace cache: {e}")
def fetch_marketplace_plugins(self, force_refresh: bool = False) -> List[Dict[str, Any]]:
"""
Fetch plugins from marketplace
Args:
force_refresh: Force refresh of cache
Returns:
List of plugin information
"""
# Check cache first
if not force_refresh and self.cache.get("plugins"):
return list(self.cache["plugins"].values())
try:
# Fetch from GitHub API (example)
response = requests.get(self.marketplace_url, timeout=10)
if response.status_code == 200:
plugins = []
for item in response.json():
if item.get("type") == "file" and item.get("name", "").endswith(".py"):
plugin_info = {
"name": item["name"].replace(".py", ""),
"download_url": item.get("download_url", ""),
"size": item.get("size", 0),
"description": f"Plugin from marketplace: {item['name']}"
}
plugins.append(plugin_info)
# Update cache
self.cache["plugins"] = {p["name"]: p for p in plugins}
self.cache["last_updated"] = str(Path(__file__).stat().st_mtime)
self._save_cache()
return plugins
else:
print(f"Warning: Could not fetch marketplace plugins: HTTP {response.status_code}")
return []
except Exception as e:
print(f"Warning: Could not fetch marketplace plugins: {e}")
return []
def get_installed_plugins(self) -> List[str]:
"""
Get list of installed plugins
Returns:
List of installed plugin names
"""
installed = []
# Check local plugins directory
for file_path in self.local_plugins_dir.glob("*.py"):
if file_path.name != "__init__.py":
installed.append(file_path.stem)
# Check built-in plugins directory
builtin_dir = Path(__file__).parent.parent / "plugins"
if builtin_dir.exists():
for file_path in builtin_dir.glob("*.py"):
if file_path.name not in ["__init__.py", "base.py", "manager.py"]:
installed.append(file_path.stem)
return installed
def install_plugin(self, plugin_name: str) -> bool:
"""
Install a plugin from marketplace
Args:
plugin_name: Name of the plugin to install
Returns:
True if successful, False otherwise
"""
# Check if already installed
if plugin_name in self.get_installed_plugins():
print(f"Plugin '{plugin_name}' is already installed")
return True
# Get plugin info from marketplace
plugins = self.fetch_marketplace_plugins()
plugin_info = None
for plugin in plugins:
if plugin["name"] == plugin_name:
plugin_info = plugin
break
if not plugin_info:
print(f"Plugin '{plugin_name}' not found in marketplace")
return False
try:
# Download plugin
download_url = plugin_info.get("download_url")
if not download_url:
print(f"No download URL for plugin '{plugin_name}'")
return False
response = requests.get(download_url, timeout=30)
if response.status_code == 200:
# Save plugin file
plugin_file = self.local_plugins_dir / f"{plugin_name}.py"
with open(plugin_file, 'w') as f:
f.write(response.text)
print(f"✅ Plugin '{plugin_name}' installed successfully")
return True
else:
print(f"Failed to download plugin: HTTP {response.status_code}")
return False
except Exception as e:
print(f"Error installing plugin: {e}")
return False
def uninstall_plugin(self, plugin_name: str) -> bool:
"""
Uninstall a plugin
Args:
plugin_name: Name of the plugin to uninstall
Returns:
True if successful, False otherwise
"""
# Check if it's a built-in plugin
builtin_dir = Path(__file__).parent.parent / "plugins"
builtin_plugins = []
if builtin_dir.exists():
for file_path in builtin_dir.glob("*.py"):
if file_path.name not in ["__init__.py", "base.py", "manager.py"]:
builtin_plugins.append(file_path.stem)
if plugin_name in builtin_plugins:
print(f"Cannot uninstall built-in plugin '{plugin_name}'")
return False
# Check if installed in local directory
plugin_file = self.local_plugins_dir / f"{plugin_name}.py"
if not plugin_file.exists():
print(f"Plugin '{plugin_name}' is not installed")
return False
try:
plugin_file.unlink()
print(f"✅ Plugin '{plugin_name}' uninstalled successfully")
return True
except Exception as e:
print(f"Error uninstalling plugin: {e}")
return False
def get_plugin_info(self, plugin_name: str) -> Optional[Dict[str, Any]]:
"""
Get information about a plugin
Args:
plugin_name: Name of the plugin
Returns:
Plugin information or None if not found
"""
# Check marketplace
plugins = self.fetch_marketplace_plugins()
for plugin in plugins:
if plugin["name"] == plugin_name:
return plugin
# Check local installation
local_file = self.local_plugins_dir / f"{plugin_name}.py"
if local_file.exists():
return {
"name": plugin_name,
"installed": True,
"location": str(local_file),
"description": f"Locally installed plugin: {plugin_name}"
}
return None
def search_plugins(self, query: str) -> List[Dict[str, Any]]:
"""
Search for plugins in marketplace
Args:
query: Search query
Returns:
List of matching plugins
"""
plugins = self.fetch_marketplace_plugins()
results = []
query_lower = query.lower()
for plugin in plugins:
if (query_lower in plugin["name"].lower() or
query_lower in plugin.get("description", "").lower()):
results.append(plugin)
return results
def get_marketplace_stats(self) -> Dict[str, Any]:
"""
Get marketplace statistics
Returns:
Dictionary with marketplace stats
"""
plugins = self.fetch_marketplace_plugins()
installed = self.get_installed_plugins()
return {
"total_plugins": len(plugins),
"installed_plugins": len(installed),
"available_plugins": len(plugins) - len(installed),
"last_updated": self.cache.get("last_updated", "Never")
}
# Global marketplace manager instance
marketplace_manager = MarketplaceManager()
+12 -12
View File
@@ -1,5 +1,5 @@
"""
Monitoring and logging for MaxClaw Agent
Monitoring and logging for LangCode Agent
"""
import os
import sys
@@ -14,18 +14,18 @@ from pathlib import Path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
class MaxClawLogger:
"""Custom logger for MaxClaw Agent"""
class LangCodeLogger:
"""Custom logger for LangCode Agent"""
def __init__(self, log_dir: Optional[str] = None):
"""
Initialize logger
Args:
log_dir: Directory for log files. If None, uses ~/.maxclaw/logs
log_dir: Directory for log files. If None, uses ~/.langcode/logs
"""
if log_dir is None:
self.log_dir = Path.home() / ".maxclaw" / "logs"
self.log_dir = Path.home() / ".langcode" / "logs"
else:
self.log_dir = Path(log_dir)
@@ -46,7 +46,7 @@ class MaxClawLogger:
)
# Create file handler
log_file = self.log_dir / f"maxclaw_{datetime.now().strftime('%Y%m%d')}.log"
log_file = self.log_dir / f"langcode_{datetime.now().strftime('%Y%m%d')}.log"
file_handler = logging.FileHandler(log_file)
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(file_formatter)
@@ -63,10 +63,10 @@ class MaxClawLogger:
)
# Create specific loggers
self.agent_logger = logging.getLogger("maxclaw.agent")
self.config_logger = logging.getLogger("maxclaw.config")
self.api_logger = logging.getLogger("maxclaw.api")
self.tools_logger = logging.getLogger("maxclaw.tools")
self.agent_logger = logging.getLogger("langcode.agent")
self.config_logger = logging.getLogger("langcode.config")
self.api_logger = logging.getLogger("langcode.api")
self.tools_logger = logging.getLogger("langcode.tools")
print(f"✅ Logging initialized. Logs will be saved to: {self.log_dir}")
@@ -187,7 +187,7 @@ class SystemMonitor:
# Global instances
logger = MaxClawLogger()
logger = LangCodeLogger()
system_monitor = SystemMonitor()
@@ -241,7 +241,7 @@ except ImportError:
if __name__ == "__main__":
# Test logging
print("Testing MaxClaw logging system...")
print("Testing LangCode logging system...")
# Log some test messages
logger.log_agent_action("test_action", {"test": "data"})
+222 -8
View File
@@ -1,18 +1,209 @@
"""
Performance optimizations for MaxClaw Agent
Performance optimizations for LangCode Agent
Enhanced with connection pooling, lazy loading, and advanced caching
"""
import os
import sys
import time
import functools
from typing import Dict, Any, Optional, Callable
import threading
from typing import Dict, Any, Optional, Callable, List
from pathlib import Path
import json
from concurrent.futures import ThreadPoolExecutor, as_completed
# Add current directory to path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
class ConnectionPool:
"""Connection pool for managing API connections"""
def __init__(self, max_connections: int = 10):
"""
Initialize connection pool
Args:
max_connections: Maximum number of connections in the pool
"""
self.max_connections = max_connections
self.connections = {}
self.lock = threading.Lock()
def get_connection(self, provider: str, **kwargs):
"""
Get a connection from the pool
Args:
provider: Provider name
**kwargs: Connection parameters
Returns:
Connection object
"""
with self.lock:
key = f"{provider}:{str(sorted(kwargs.items()))}"
if key not in self.connections:
# Create new connection
if provider == "ollama":
import requests
base_url = kwargs.get("base_url", "http://localhost:11434")
session = requests.Session()
session.headers.update({"Content-Type": "application/json"})
self.connections[key] = {
"session": session,
"base_url": base_url,
"last_used": time.time()
}
elif provider in ["openai", "openrouter", "anthropic", "google"]:
import requests
session = requests.Session()
api_key = kwargs.get("api_key", "")
if api_key:
session.headers.update({"Authorization": f"Bearer {api_key}"})
self.connections[key] = {
"session": session,
"last_used": time.time()
}
# Update last used time
if key in self.connections:
self.connections[key]["last_used"] = time.time()
return self.connections.get(key)
def cleanup_idle_connections(self, max_idle_time: int = 300):
"""
Clean up idle connections
Args:
max_idle_time: Maximum idle time in seconds
"""
with self.lock:
current_time = time.time()
keys_to_remove = []
for key, conn in self.connections.items():
if current_time - conn["last_used"] > max_idle_time:
keys_to_remove.append(key)
for key in keys_to_remove:
del self.connections[key]
def get_stats(self) -> Dict[str, Any]:
"""Get connection pool statistics"""
with self.lock:
return {
"total_connections": len(self.connections),
"max_connections": self.max_connections,
"connections": list(self.connections.keys())
}
class LazyLoader:
"""Lazy loading for heavy resources"""
def __init__(self):
self.loaded_modules = {}
self.loaded_tools = {}
self.lock = threading.Lock()
def load_module(self, module_name: str, import_path: str):
"""
Lazily load a module
Args:
module_name: Name to store the module under
import_path: Import path for the module
Returns:
Loaded module
"""
with self.lock:
if module_name not in self.loaded_modules:
try:
module = __import__(import_path, fromlist=[''])
self.loaded_modules[module_name] = module
except ImportError as e:
print(f"Warning: Could not load module {module_name}: {e}")
self.loaded_modules[module_name] = None
return self.loaded_modules.get(module_name)
def load_tool(self, tool_name: str, loader_func: Callable):
"""
Lazily load a tool
Args:
tool_name: Name to store the tool under
loader_func: Function to load the tool
Returns:
Loaded tool
"""
with self.lock:
if tool_name not in self.loaded_tools:
try:
tool = loader_func()
self.loaded_tools[tool_name] = tool
except Exception as e:
print(f"Warning: Could not load tool {tool_name}: {e}")
self.loaded_tools[tool_name] = None
return self.loaded_tools.get(tool_name)
def clear(self):
"""Clear all loaded resources"""
with self.lock:
self.loaded_modules.clear()
self.loaded_tools.clear()
class BatchProcessor:
"""Batch processing for multiple operations"""
def __init__(self, max_workers: int = 5):
"""
Initialize batch processor
Args:
max_workers: Maximum number of worker threads
"""
self.max_workers = max_workers
self.executor = ThreadPoolExecutor(max_workers=max_workers)
def process_batch(self, tasks: List[Callable], timeout: int = 30) -> List[Any]:
"""
Process a batch of tasks in parallel
Args:
tasks: List of callable tasks
timeout: Timeout in seconds
Returns:
List of results
"""
futures = [self.executor.submit(task) for task in tasks]
results = []
try:
for future in as_completed(futures, timeout=timeout):
try:
result = future.result()
results.append(result)
except Exception as e:
results.append({"error": str(e)})
except Exception as e:
print(f"Batch processing error: {e}")
return results
def shutdown(self):
"""Shutdown the executor"""
self.executor.shutdown(wait=True)
class PerformanceMonitor:
"""Monitor and optimize performance"""
@@ -20,6 +211,9 @@ class PerformanceMonitor:
self.metrics = {}
self.cache = {}
self.cache_ttl = 300 # 5 minutes default TTL
self.connection_pool = ConnectionPool()
self.lazy_loader = LazyLoader()
self.batch_processor = BatchProcessor()
def timing(self, func: Callable) -> Callable:
"""Decorator to measure function execution time"""
@@ -90,6 +284,20 @@ class PerformanceMonitor:
def clear_metrics(self):
"""Clear performance metrics"""
self.metrics.clear()
def get_connection_pool_stats(self) -> Dict[str, Any]:
"""Get connection pool statistics"""
return self.connection_pool.get_stats()
def cleanup_resources(self):
"""Clean up resources"""
self.connection_pool.cleanup_idle_connections()
self.clear_cache()
def shutdown(self):
"""Shutdown all resources"""
self.batch_processor.shutdown()
self.cleanup_resources()
# Global performance monitor instance
@@ -171,26 +379,26 @@ def optimize_provider_tools():
def optimize_agent_creation():
"""Optimize agent creation with caching"""
from main import create_maxclaw_agent
from main import create_langcode_agent
# Cache agent creation
original_create_maxclaw_agent = create_maxclaw_agent
original_create_langcode_agent = create_langcode_agent
@functools.wraps(original_create_maxclaw_agent)
def optimized_create_maxclaw_agent(system_prompt=None):
@functools.wraps(original_create_langcode_agent)
def optimized_create_langcode_agent(system_prompt=None):
cache_key = f"agent:{system_prompt}"
if cache_key in performance_monitor.cache:
cached_time, cached_result = performance_monitor.cache[cache_key]
if time.time() - cached_time < 600: # Cache for 10 minutes
return cached_result
result = original_create_maxclaw_agent(system_prompt)
result = original_create_langcode_agent(system_prompt)
performance_monitor.cache[cache_key] = (time.time(), result)
return result
# Replace function
import main
main.create_maxclaw_agent = optimized_create_maxclaw_agent
main.create_langcode_agent = optimized_create_langcode_agent
print("✅ Agent creation optimized with caching")
@@ -288,6 +496,12 @@ def get_performance_report() -> str:
report += f"\nCache Statistics:\n"
report += f" Cached Items: {cache_size}\n"
# Connection pool statistics
pool_stats = performance_monitor.get_connection_pool_stats()
report += f"\nConnection Pool:\n"
report += f" Total Connections: {pool_stats['total_connections']}\n"
report += f" Max Connections: {pool_stats['max_connections']}\n"
return report
+1 -1
View File
@@ -1,5 +1,5 @@
"""
MaxClaw Agent Plugins Package
LangCode Agent Plugins Package
Provides plugin system for extensibility
"""
+2 -2
View File
@@ -1,5 +1,5 @@
"""
Base plugin class for MaxClaw Agent
Base plugin class for LangCode Agent
"""
from abc import ABC, abstractmethod
from typing import List, Dict, Any, Optional
@@ -7,7 +7,7 @@ from langchain.tools import Tool
class BasePlugin(ABC):
"""Base class for MaxClaw plugins"""
"""Base class for LangCode plugins"""
def __init__(self, name: str, version: str = "1.0.0", description: str = ""):
"""
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Example plugin for MaxClaw Agent
Example plugin for LangCode Agent
Demonstrates how to create a plugin
"""
import os
+441
View File
@@ -0,0 +1,441 @@
"""
File tools plugin for LangCode Agent
Provides additional file management tools
"""
import os
import sys
import shutil
from typing import List, Dict, Any
from pathlib import Path
from langchain.tools import Tool
# Add parent directory to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from plugins.base import BasePlugin
class FileToolsPlugin(BasePlugin):
"""Plugin providing additional file management tools"""
def __init__(self):
"""Initialize file tools plugin"""
super().__init__(
name="file_tools",
version="1.0.0",
description="Additional file management tools for LangCode Agent"
)
def get_tools(self) -> List[Tool]:
"""Get tools provided by this plugin"""
return [
Tool(
name="file_find",
func=self.find_files,
description="Find files by name pattern"
),
Tool(
name="file_size",
func=self.get_file_size,
description="Get file or directory size"
),
Tool(
name="file_copy",
func=self.copy_file,
description="Copy a file to a new location"
),
Tool(
name="file_move",
func=self.move_file,
description="Move a file to a new location"
),
Tool(
name="file_delete",
func=self.delete_file,
description="Delete a file or directory"
),
Tool(
name="file_info",
func=self.get_file_info,
description="Get detailed file information"
),
Tool(
name="dir_list",
func=self.list_directory,
description="List directory contents with details"
),
Tool(
name="dir_create",
func=self.create_directory,
description="Create a new directory"
)
]
def get_commands(self) -> Dict[str, callable]:
"""Get commands provided by this plugin"""
return {
"/find": self.find_command,
"/size": self.size_command,
"/copy": self.copy_command,
"/move": self.move_command,
"/delete": self.delete_command,
"/info": self.info_command,
"/dir": self.dir_command,
"/mkdir": self.mkdir_command
}
def find_files(self, pattern: str, directory: str = ".") -> str:
"""Find files by name pattern"""
try:
path = Path(directory)
if not path.exists():
return f"❌ Directory not found: {directory}"
matches = list(path.rglob(pattern))
if not matches:
return f"No files found matching '{pattern}' in {directory}"
output = f"🔍 Found {len(matches)} files matching '{pattern}':\n"
output += "=" * 50 + "\n"
for match in matches[:20]: # Limit to 20 results
if match.is_file():
size = match.stat().st_size
if size > 1024 * 1024:
size_str = f"{size / (1024 * 1024):.1f} MB"
elif size > 1024:
size_str = f"{size / 1024:.1f} KB"
else:
size_str = f"{size} B"
output += f"📄 {match.relative_to(path)} ({size_str})\n"
if len(matches) > 20:
output += f"\n... and {len(matches) - 20} more files"
return output
except Exception as e:
return f"❌ Error finding files: {str(e)}"
def get_file_size(self, path: str) -> str:
"""Get file or directory size"""
try:
path_obj = Path(path)
if not path_obj.exists():
return f"❌ Path not found: {path}"
if path_obj.is_file():
size = path_obj.stat().st_size
if size > 1024 * 1024 * 1024:
size_str = f"{size / (1024 * 1024 * 1024):.2f} GB"
elif size > 1024 * 1024:
size_str = f"{size / (1024 * 1024):.2f} MB"
elif size > 1024:
size_str = f"{size / 1024:.2f} KB"
else:
size_str = f"{size} bytes"
return f"📄 {path}: {size_str}"
elif path_obj.is_dir():
total_size = 0
file_count = 0
for item in path_obj.rglob('*'):
if item.is_file():
total_size += item.stat().st_size
file_count += 1
if total_size > 1024 * 1024 * 1024:
size_str = f"{total_size / (1024 * 1024 * 1024):.2f} GB"
elif total_size > 1024 * 1024:
size_str = f"{total_size / (1024 * 1024):.2f} MB"
elif total_size > 1024:
size_str = f"{total_size / 1024:.2f} KB"
else:
size_str = f"{total_size} bytes"
return f"📁 {path}: {size_str} ({file_count} files)"
else:
return f"❌ Unknown path type: {path}"
except Exception as e:
return f"❌ Error getting size: {str(e)}"
def copy_file(self, source: str, destination: str) -> str:
"""Copy a file to a new location"""
try:
source_path = Path(source)
dest_path = Path(destination)
if not source_path.exists():
return f"❌ Source not found: {source}"
# If destination is a directory, copy into it
if dest_path.is_dir():
dest_path = dest_path / source_path.name
# Create parent directories if needed
dest_path.parent.mkdir(parents=True, exist_ok=True)
if source_path.is_file():
shutil.copy2(source_path, dest_path)
return f"✅ File copied: {source} → {destination}"
elif source_path.is_dir():
shutil.copytree(source_path, dest_path)
return f"✅ Directory copied: {source} → {destination}"
else:
return f"❌ Unknown source type: {source}"
except Exception as e:
return f"❌ Error copying: {str(e)}"
def move_file(self, source: str, destination: str) -> str:
"""Move a file to a new location"""
try:
source_path = Path(source)
dest_path = Path(destination)
if not source_path.exists():
return f"❌ Source not found: {source}"
# If destination is a directory, move into it
if dest_path.is_dir():
dest_path = dest_path / source_path.name
# Create parent directories if needed
dest_path.parent.mkdir(parents=True, exist_ok=True)
shutil.move(str(source_path), str(dest_path))
return f"✅ Moved: {source} → {destination}"
except Exception as e:
return f"❌ Error moving: {str(e)}"
def delete_file(self, path: str, confirm: bool = True) -> str:
"""Delete a file or directory"""
try:
path_obj = Path(path)
if not path_obj.exists():
return f"❌ Path not found: {path}"
if confirm:
return f"⚠️ To delete {path}, use the command with confirm=True"
if path_obj.is_file():
path_obj.unlink()
return f"✅ File deleted: {path}"
elif path_obj.is_dir():
shutil.rmtree(path_obj)
return f"✅ Directory deleted: {path}"
else:
return f"❌ Unknown path type: {path}"
except Exception as e:
return f"❌ Error deleting: {str(e)}"
def get_file_info(self, path: str) -> str:
"""Get detailed file information"""
try:
path_obj = Path(path)
if not path_obj.exists():
return f"❌ Path not found: {path}"
stat = path_obj.stat()
# Format size
size = stat.st_size
if size > 1024 * 1024 * 1024:
size_str = f"{size / (1024 * 1024 * 1024):.2f} GB"
elif size > 1024 * 1024:
size_str = f"{size / (1024 * 1024):.2f} MB"
elif size > 1024:
size_str = f"{size / 1024:.2f} KB"
else:
size_str = f"{size} bytes"
# Format dates
from datetime import datetime
created = datetime.fromtimestamp(stat.st_ctime).strftime('%Y-%m-%d %H:%M:%S')
modified = datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S')
accessed = datetime.fromtimestamp(stat.st_atime).strftime('%Y-%m-%d %H:%M:%S')
# File type
if path_obj.is_file():
file_type = "File"
elif path_obj.is_dir():
file_type = "Directory"
elif path_obj.is_symlink():
file_type = "Symbolic Link"
else:
file_type = "Unknown"
output = f"📋 File Information: {path}\n"
output += "=" * 50 + "\n"
output += f"Type: {file_type}\n"
output += f"Size: {size_str}\n"
output += f"Created: {created}\n"
output += f"Modified: {modified}\n"
output += f"Accessed: {accessed}\n"
output += f"Permissions: {oct(stat.st_mode)[-3:]}\n"
if path_obj.is_file():
output += f"Extension: {path_obj.suffix}\n"
output += f"Name: {path_obj.name}\n"
output += f"Parent: {path_obj.parent}\n"
return output
except Exception as e:
return f"❌ Error getting file info: {str(e)}"
def list_directory(self, path: str = ".", show_hidden: bool = False) -> str:
"""List directory contents with details"""
try:
path_obj = Path(path)
if not path_obj.exists():
return f"❌ Directory not found: {path}"
if not path_obj.is_dir():
return f"❌ Not a directory: {path}"
items = list(path_obj.iterdir())
if not show_hidden:
items = [item for item in items if not item.name.startswith('.')]
if not items:
return f"📁 Directory is empty: {path}"
output = f"📁 Directory: {path}\n"
output += "=" * 50 + "\n"
# Sort items: directories first, then files
dirs = [item for item in items if item.is_dir()]
files = [item for item in items if item.is_file()]
# List directories
for dir_item in sorted(dirs, key=lambda x: x.name.lower()):
try:
size = sum(f.stat().st_size for f in dir_item.rglob('*') if f.is_file())
if size > 1024 * 1024:
size_str = f"{size / (1024 * 1024):.1f} MB"
elif size > 1024:
size_str = f"{size / 1024:.1f} KB"
else:
size_str = f"{size} B"
output += f"📁 {dir_item.name}/ ({size_str})\n"
except:
output += f"📁 {dir_item.name}/\n"
# List files
for file_item in sorted(files, key=lambda x: x.name.lower()):
try:
size = file_item.stat().st_size
if size > 1024 * 1024:
size_str = f"{size / (1024 * 1024):.1f} MB"
elif size > 1024:
size_str = f"{size / 1024:.1f} KB"
else:
size_str = f"{size} B"
output += f"📄 {file_item.name} ({size_str})\n"
except:
output += f"📄 {file_item.name}\n"
output += f"\nTotal: {len(dirs)} directories, {len(files)} files"
return output
except Exception as e:
return f"❌ Error listing directory: {str(e)}"
def create_directory(self, path: str, parents: bool = True) -> str:
"""Create a new directory"""
try:
path_obj = Path(path)
if path_obj.exists():
return f"⚠️ Path already exists: {path}"
path_obj.mkdir(parents=parents, exist_ok=True)
return f"✅ Directory created: {path}"
except Exception as e:
return f"❌ Error creating directory: {str(e)}"
# Command implementations
def find_command(self, args: List[str] = None) -> str:
"""Find command implementation"""
if not args:
return "❌ Usage: /find <pattern> [directory]"
pattern = args[0]
directory = args[1] if len(args) > 1 else "."
return self.find_files(pattern, directory)
def size_command(self, args: List[str] = None) -> str:
"""Size command implementation"""
if not args:
return "❌ Usage: /size <path>"
return self.get_file_size(args[0])
def copy_command(self, args: List[str] = None) -> str:
"""Copy command implementation"""
if not args or len(args) < 2:
return "❌ Usage: /copy <source> <destination>"
return self.copy_file(args[0], args[1])
def move_command(self, args: List[str] = None) -> str:
"""Move command implementation"""
if not args or len(args) < 2:
return "❌ Usage: /move <source> <destination>"
return self.move_file(args[0], args[1])
def delete_command(self, args: List[str] = None) -> str:
"""Delete command implementation"""
if not args:
return "❌ Usage: /delete <path>"
return self.delete_file(args[0], confirm=False)
def info_command(self, args: List[str] = None) -> str:
"""Info command implementation"""
if not args:
return "❌ Usage: /info <path>"
return self.get_file_info(args[0])
def dir_command(self, args: List[str] = None) -> str:
"""Dir command implementation"""
path = args[0] if args else "."
show_hidden = "-a" in args if args else False
return self.list_directory(path, show_hidden)
def mkdir_command(self, args: List[str] = None) -> str:
"""Mkdir command implementation"""
if not args:
return "❌ Usage: /mkdir <path>"
return self.create_directory(args[0])
def on_load(self):
"""Called when plugin is loaded"""
print(f"📦 File tools plugin loaded: {self.name} v{self.version}")
def on_unload(self):
"""Called when plugin is unloaded"""
print(f"🗑️ File tools plugin unloaded: {self.name}")
+2 -2
View File
@@ -1,5 +1,5 @@
"""
Plugin manager for MaxClaw Agent
Plugin manager for LangCode Agent
"""
import os
import sys
@@ -15,7 +15,7 @@ from plugins.base import BasePlugin
class PluginManager:
"""Manages plugins for MaxClaw Agent"""
"""Manages plugins for LangCode Agent"""
def __init__(self, plugin_dir: Optional[str] = None):
"""
+421
View File
@@ -0,0 +1,421 @@
"""
System utilities plugin for LangCode Agent
Provides system information and utility tools
"""
import os
import sys
import platform
import socket
import subprocess
from typing import List, Dict, Any
from datetime import datetime
from langchain.tools import Tool
# Add parent directory to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from plugins.base import BasePlugin
class SystemUtilsPlugin(BasePlugin):
"""Plugin providing system utilities and information"""
def __init__(self):
"""Initialize system utilities plugin"""
super().__init__(
name="system_utils",
version="1.0.0",
description="System information and utility tools"
)
def get_tools(self) -> List[Tool]:
"""Get tools provided by this plugin"""
return [
Tool(
name="system_info",
func=self.get_system_info,
description="Get comprehensive system information"
),
Tool(
name="system_uptime",
func=self.get_system_uptime,
description="Get system uptime"
),
Tool(
name="system_ip",
func=self.get_ip_address,
description="Get system IP addresses"
),
Tool(
name="system_hostname",
func=self.get_hostname,
description="Get system hostname"
),
Tool(
name="system_platform",
func=self.get_platform_info,
description="Get platform information"
),
Tool(
name="system_users",
func=self.get_logged_users,
description="Get logged in users"
),
Tool(
name="system_processes",
func=self.get_running_processes,
description="Get running processes"
),
Tool(
name="system_disk_usage",
func=self.get_disk_usage,
description="Get disk usage information"
)
]
def get_commands(self) -> Dict[str, callable]:
"""Get commands provided by this plugin"""
return {
"/sysinfo": self.sysinfo_command,
"/uptime": self.uptime_command,
"/ip": self.ip_command,
"/hostname": self.hostname_command,
"/platform": self.platform_command,
"/users": self.users_command,
"/ps": self.ps_command,
"/df": self.df_command
}
def get_system_info(self) -> str:
"""Get comprehensive system information"""
try:
info = {
"System": platform.system(),
"Node Name": platform.node(),
"Release": platform.release(),
"Version": platform.version(),
"Machine": platform.machine(),
"Processor": platform.processor(),
"Python Version": platform.python_version(),
"Hostname": socket.gethostname(),
"IP Address": socket.gethostbyname(socket.gethostname()),
"Current User": os.getlogin(),
"Current Directory": os.getcwd(),
"Home Directory": os.path.expanduser("~"),
"Timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
output = "💻 System Information:\n"
output += "=" * 50 + "\n"
for key, value in info.items():
output += f"{key}: {value}\n"
return output
except Exception as e:
return f"❌ Error getting system info: {str(e)}"
def get_system_uptime(self) -> str:
"""Get system uptime"""
try:
if platform.system() == "Linux":
with open('/proc/uptime', 'r') as f:
uptime_seconds = float(f.readline().split()[0])
days = int(uptime_seconds // (24 * 3600))
hours = int((uptime_seconds % (24 * 3600)) // 3600)
minutes = int((uptime_seconds % 3600) // 60)
seconds = int(uptime_seconds % 60)
if days > 0:
return f"⏱️ System uptime: {days} days, {hours} hours, {minutes} minutes, {seconds} seconds"
elif hours > 0:
return f"⏱️ System uptime: {hours} hours, {minutes} minutes, {seconds} seconds"
elif minutes > 0:
return f"⏱️ System uptime: {minutes} minutes, {seconds} seconds"
else:
return f"⏱️ System uptime: {seconds} seconds"
elif platform.system() == "Darwin": # macOS
result = subprocess.run(['uptime'], capture_output=True, text=True)
return f"⏱️ {result.stdout.strip()}"
elif platform.system() == "Windows":
result = subprocess.run(['net', 'stats', 'srv'], capture_output=True, text=True)
# Parse Windows uptime from output
for line in result.stdout.split('\n'):
if 'Statistics since' in line:
return f"⏱️ {line.strip()}"
return "⏱️ Uptime information not available"
else:
return "⏱️ Uptime information not available for this platform"
except Exception as e:
return f"❌ Error getting uptime: {str(e)}"
def get_ip_address(self) -> str:
"""Get system IP addresses"""
try:
hostname = socket.gethostname()
# Get all IP addresses
ip_addresses = []
# Get hostname IP
try:
host_ip = socket.gethostbyname(hostname)
ip_addresses.append(("Hostname", host_ip))
except:
pass
# Get all network interfaces (if available)
try:
import netifaces
for interface in netifaces.interfaces():
addrs = netifaces.ifaddresses(interface)
if netifaces.AF_INET in addrs:
for addr in addrs[netifaces.AF_INET]:
ip_addresses.append((interface, addr['addr']))
except ImportError:
# netifaces not available, use alternative method
pass
if not ip_addresses:
return "❌ No IP addresses found"
output = "🌐 IP Addresses:\n"
output += "=" * 30 + "\n"
for interface, ip in ip_addresses:
output += f"{interface}: {ip}\n"
return output
except Exception as e:
return f"❌ Error getting IP addresses: {str(e)}"
def get_hostname(self) -> str:
"""Get system hostname"""
try:
hostname = socket.gethostname()
fqdn = socket.getfqdn()
output = "🏠 Hostname Information:\n"
output += "=" * 30 + "\n"
output += f"Hostname: {hostname}\n"
output += f"FQDN: {fqdn}\n"
return output
except Exception as e:
return f"❌ Error getting hostname: {str(e)}"
def get_platform_info(self) -> str:
"""Get platform information"""
try:
info = {
"System": platform.system(),
"Node": platform.node(),
"Release": platform.release(),
"Version": platform.version(),
"Machine": platform.machine(),
"Processor": platform.processor(),
"Python Build": platform.python_build(),
"Python Compiler": platform.python_compiler(),
"Python Implementation": platform.python_implementation(),
"Python Version": platform.python_version()
}
output = "🖥️ Platform Information:\n"
output += "=" * 50 + "\n"
for key, value in info.items():
output += f"{key}: {value}\n"
return output
except Exception as e:
return f"❌ Error getting platform info: {str(e)}"
def get_logged_users(self) -> str:
"""Get logged in users"""
try:
if platform.system() in ["Linux", "Darwin"]:
result = subprocess.run(['who'], capture_output=True, text=True)
users = result.stdout.strip()
if not users:
return "👥 No users currently logged in"
output = "👥 Logged in users:\n"
output += "=" * 30 + "\n"
output += users
return output
elif platform.system() == "Windows":
result = subprocess.run(['query', 'user'], capture_output=True, text=True)
users = result.stdout.strip()
if not users:
return "👥 No users currently logged in"
output = "👥 Logged in users:\n"
output += "=" * 30 + "\n"
output += users
return output
else:
return "👥 User information not available for this platform"
except Exception as e:
return f"❌ Error getting logged users: {str(e)}"
def get_running_processes(self, limit: int = 20) -> str:
"""Get running processes"""
try:
if platform.system() in ["Linux", "Darwin"]:
result = subprocess.run(['ps', 'aux'], capture_output=True, text=True)
processes = result.stdout.strip().split('\n')
if len(processes) <= 1:
return "🔄 No processes found"
output = f"🔄 Running processes (top {limit}):\n"
output += "=" * 50 + "\n"
# Header
output += processes[0] + "\n"
output += "-" * 50 + "\n"
# Processes (limit to specified number)
for process in processes[1:limit+1]:
output += process + "\n"
if len(processes) > limit+1:
output += f"\n... and {len(processes) - limit - 1} more processes"
return output
elif platform.system() == "Windows":
result = subprocess.run(['tasklist'], capture_output=True, text=True)
processes = result.stdout.strip().split('\n')
if len(processes) <= 3:
return "🔄 No processes found"
output = f"🔄 Running processes (top {limit}):\n"
output += "=" * 50 + "\n"
# Header (first 3 lines in Windows)
for i in range(3):
if i < len(processes):
output += processes[i] + "\n"
output += "-" * 50 + "\n"
# Processes (limit to specified number)
for process in processes[3:limit+3]:
output += process + "\n"
if len(processes) > limit+3:
output += f"\n... and {len(processes) - limit - 3} more processes"
return output
else:
return "🔄 Process information not available for this platform"
except Exception as e:
return f"❌ Error getting processes: {str(e)}"
def get_disk_usage(self, path: str = "/") -> str:
"""Get disk usage information"""
try:
if platform.system() == "Windows":
# For Windows, use different drive letters
if path == "/":
path = "C:\\"
import shutil
total, used, free = shutil.disk_usage(path)
# Convert to human readable
def format_bytes(bytes):
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
if bytes < 1024.0:
return f"{bytes:.2f} {unit}"
bytes /= 1024.0
return f"{bytes:.2f} PB"
total_str = format_bytes(total)
used_str = format_bytes(used)
free_str = format_bytes(free)
percent_used = (used / total) * 100
output = f"💾 Disk Usage for {path}:\n"
output += "=" * 40 + "\n"
output += f"Total: {total_str}\n"
output += f"Used: {used_str} ({percent_used:.1f}%)\n"
output += f"Free: {free_str}\n"
# Create a simple progress bar
bar_length = 30
filled_length = int(bar_length * percent_used // 100)
bar = '█' * filled_length + '░' * (bar_length - filled_length)
output += f"\n[{bar}] {percent_used:.1f}%\n"
return output
except Exception as e:
return f"❌ Error getting disk usage: {str(e)}"
# Command implementations
def sysinfo_command(self, args: List[str] = None) -> str:
"""Sysinfo command implementation"""
return self.get_system_info()
def uptime_command(self, args: List[str] = None) -> str:
"""Uptime command implementation"""
return self.get_system_uptime()
def ip_command(self, args: List[str] = None) -> str:
"""IP command implementation"""
return self.get_ip_address()
def hostname_command(self, args: List[str] = None) -> str:
"""Hostname command implementation"""
return self.get_hostname()
def platform_command(self, args: List[str] = None) -> str:
"""Platform command implementation"""
return self.get_platform_info()
def users_command(self, args: List[str] = None) -> str:
"""Users command implementation"""
return self.get_logged_users()
def ps_command(self, args: List[str] = None) -> str:
"""PS command implementation"""
limit = 20
if args and args[0].isdigit():
limit = int(args[0])
return self.get_running_processes(limit)
def df_command(self, args: List[str] = None) -> str:
"""DF command implementation"""
path = args[0] if args else "/"
return self.get_disk_usage(path)
def on_load(self):
"""Called when plugin is loaded"""
print(f"📦 System utils plugin loaded: {self.name} v{self.version}")
def on_unload(self):
"""Called when plugin is unloaded"""
print(f"🗑️ System utils plugin unloaded: {self.name}")
+2 -2
View File
@@ -7,13 +7,13 @@ import os
import sys
sys.path.insert(0, '.')
from main import create_maxclaw_agent
from main import create_langcode_agent
def test_agent_creation():
"""Test that the agent can be created successfully."""
print("Testing agent creation...")
try:
agent = create_maxclaw_agent()
agent = create_langcode_agent()
print("✅ Agent created successfully!")
return agent
except Exception as e:
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Test suite for MaxClaw Agent configuration manager
Test suite for LangCode Agent configuration manager
"""
import os
import json
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Test suite for MaxClaw Agent provider tools
Test suite for LangCode Agent provider tools
"""
import os
import sys
+1 -1
View File
@@ -1,5 +1,5 @@
"""
MaxClaw Agent Tools Package
LangCode Agent Tools Package
Provides custom tools for Proxmox, Ollama, and unified provider management
"""
+2 -2
View File
@@ -1,5 +1,5 @@
"""
Ollama management tools for MaxClaw Agent
Ollama management tools for LangCode Agent
Provides tools for managing Ollama models and instances
"""
import requests
@@ -309,7 +309,7 @@ class OllamaTools:
def get_ollama_tools(base_url: str = "http://localhost:11434") -> List[Tool]:
"""
Get a list of Ollama tools for the MaxClaw agent
Get a list of Ollama tools for the LangCode agent
Args:
base_url: Base URL of the Ollama instance
+1 -1
View File
@@ -1,5 +1,5 @@
"""
Unified Provider Tools for MaxClaw Agent
Unified Provider Tools for LangCode Agent
Provides a consistent interface for managing different LLM providers
"""
import os
+2 -2
View File
@@ -1,5 +1,5 @@
"""
Proxmox management tools for MaxClaw Agent
Proxmox management tools for LangCode Agent
Provides tools for managing Proxmox virtual machines and containers
"""
import subprocess
@@ -266,7 +266,7 @@ class ProxmoxTools:
def get_proxmox_tools(host: str = "192.168.0.100", user: str = "root",
password: Optional[str] = None, api_token: Optional[str] = None) -> List[Tool]:
"""
Get a list of Proxmox tools for the MaxClaw agent
Get a list of Proxmox tools for the LangCode agent
Args:
host: Proxmox host address
+365 -58
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MaxClaw Agent - Configurazione</title>
<title>LangCode Agent - Configurazione</title>
<style>
* {
margin: 0;
@@ -21,6 +21,7 @@
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}
header {
@@ -89,6 +90,8 @@
font-size: 16px;
font-weight: 600;
transition: background 0.3s;
display: inline-block;
margin: 5px;
}
.btn:hover {
@@ -122,8 +125,8 @@
padding: 15px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
flex-direction: column;
gap: 10px;
}
.config-info h3 {
@@ -139,6 +142,7 @@
.config-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.status-badge {
@@ -193,10 +197,12 @@
.chat-input {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.chat-input input {
flex: 1;
min-width: 200px;
}
.alert {
@@ -225,6 +231,7 @@
display: flex;
border-bottom: 2px solid #dee2e6;
margin-bottom: 20px;
flex-wrap: wrap;
}
.tab {
@@ -232,6 +239,9 @@
cursor: pointer;
border-bottom: 3px solid transparent;
margin-bottom: -2px;
flex: 1;
text-align: center;
min-width: 120px;
}
.tab.active {
@@ -247,71 +257,233 @@
.tab-content.active {
display: block;
}
.language-selector {
position: absolute;
top: 20px;
right: 20px;
}
.language-selector select {
padding: 8px 12px;
border-radius: 6px;
border: 1px solid #ddd;
background: white;
font-size: 14px;
cursor: pointer;
}
/* Responsive Design */
@media (max-width: 768px) {
body {
padding: 10px;
}
.container {
padding: 0 10px;
}
header h1 {
font-size: 2rem;
}
header p {
font-size: 1rem;
}
.card {
padding: 20px;
margin-bottom: 20px;
}
.card h2 {
font-size: 1.3rem;
}
.form-group {
margin-bottom: 15px;
}
label {
font-size: 14px;
}
input, select, textarea {
padding: 10px;
font-size: 14px;
}
.btn {
padding: 10px 20px;
font-size: 14px;
width: 100%;
margin: 5px 0;
}
.config-item {
padding: 12px;
}
.config-actions {
flex-direction: column;
}
.config-actions .btn {
width: 100%;
}
.chat-container {
height: 300px;
padding: 10px;
}
.message {
max-width: 90%;
padding: 8px 12px;
font-size: 14px;
}
.chat-input {
flex-direction: column;
}
.chat-input input {
min-width: 100%;
}
.tabs {
flex-direction: column;
}
.tab {
padding: 8px 15px;
border-bottom: none;
border-left: 3px solid transparent;
margin-bottom: 0;
text-align: left;
}
.tab.active {
border-bottom: none;
border-left-color: #667eea;
}
.language-selector {
position: static;
text-align: center;
margin-bottom: 20px;
}
}
@media (max-width: 480px) {
header h1 {
font-size: 1.8rem;
}
.card {
padding: 15px;
}
.btn {
padding: 8px 16px;
font-size: 13px;
}
.chat-container {
height: 250px;
}
}
/* Touch-friendly improvements */
@media (hover: none) and (pointer: coarse) {
.btn:hover {
background: #667eea;
}
.btn:active {
background: #5a6fd8;
transform: scale(0.98);
}
input:focus, select:focus, textarea:focus {
border-color: #667eea;
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
}
}
</style>
</head>
<body>
<div class="container">
<div class="language-selector">
<select id="language-select" onchange="changeLanguage(this.value)">
<option value="it">Italiano</option>
<option value="en">English</option>
</select>
</div>
<header>
<h1>MaxClaw Agent</h1>
<p>Interfaccia di configurazione per l'agente AI</p>
<h1 id="title">LangCode Agent</h1>
<p id="subtitle">Interfaccia di configurazione per l'agente AI</p>
</header>
<div class="card">
<div class="tabs">
<div class="tab active" onclick="switchTab('config')">Configurazione</div>
<div class="tab" onclick="switchTab('chat')">Chat</div>
<div class="tab" onclick="switchTab('status')">Stato</div>
<div class="tab active" onclick="switchTab('config')" id="tab-config">Configurazione</div>
<div class="tab" onclick="switchTab('chat')" id="tab-chat">Chat</div>
<div class="tab" onclick="switchTab('status')" id="tab-status">Stato</div>
</div>
<div id="config-tab" class="tab-content active">
<h2>Gestione Configurazioni</h2>
<h2 id="config-title">Gestione Configurazioni</h2>
<div id="alert-container"></div>
<form id="config-form">
<div class="form-group">
<label for="config-name">Nome Configurazione</label>
<label for="config-name" id="name-label">Nome Configurazione</label>
<input type="text" id="config-name" placeholder="es. mio-ollama" required>
</div>
<div class="form-group">
<label for="config-provider">Provider</label>
<label for="config-provider" id="provider-label">Provider</label>
<select id="config-provider" required>
<option value="">Seleziona un provider</option>
<option value="ollama">Ollama</option>
<option value="openai">OpenAI</option>
<option value="openrouter">OpenRouter</option>
<option value="anthropic">Anthropic</option>
<option value="google">Google</option>
</select>
</div>
<div class="form-group">
<label for="config-model">Nome Modello</label>
<label for="config-model" id="model-label">Nome Modello</label>
<input type="text" id="config-model" placeholder="es. gemma2:9b" required>
</div>
<div class="form-group" id="api-key-group">
<label for="config-api-key">Chiave API</label>
<label for="config-api-key" id="api-key-label">Chiave API</label>
<input type="password" id="config-api-key" placeholder="Inserisci la chiave API">
</div>
<div class="form-group" id="base-url-group">
<label for="config-base-url">URL Base</label>
<label for="config-base-url" id="base-url-label">URL Base</label>
<input type="text" id="config-base-url" placeholder="http://localhost:11434">
</div>
<div class="form-group">
<label>
<input type="checkbox" id="config-set-active">
Imposta come configurazione attiva
<span id="set-active-label">Imposta come configurazione attiva</span>
</label>
</div>
<button type="submit" class="btn">Salva Configurazione</button>
<button type="button" class="btn btn-secondary" onclick="clearForm()">Pulisci</button>
<button type="submit" class="btn" id="save-btn">Salva Configurazione</button>
<button type="button" class="btn btn-secondary" onclick="clearForm()" id="clear-btn">Pulisci</button>
</form>
<div class="config-list">
<h3>Configurazioni Esistenti</h3>
<h3 id="existing-configs-title">Configurazioni Esistenti</h3>
<div id="config-list-container">
<!-- Configurations will be loaded here -->
</div>
@@ -319,49 +491,49 @@
</div>
<div id="chat-tab" class="tab-content">
<h2>Chat con MaxClaw</h2>
<h2 id="chat-title">Chat con LangCode</h2>
<div class="chat-container" id="chat-container">
<div class="message agent-message">
Ciao! Sono MaxClaw, il tuo agente AI. Come posso aiutarti oggi?
<div class="message agent-message" id="welcome-message">
Ciao! Sono LangCode, il tuo agente AI. Come posso aiutarti oggi?
</div>
</div>
<div class="chat-input">
<input type="text" id="chat-input" placeholder="Scrivi un messaggio...">
<button class="btn" onclick="sendMessage()">Invia</button>
<button class="btn" onclick="sendMessage()" id="send-btn">Invia</button>
</div>
</div>
<div id="status-tab" class="tab-content">
<h2>Stato del Sistema</h2>
<h2 id="status-title">Stato del Sistema</h2>
<div id="status-container">
<div class="alert alert-success">
<strong>✅ Sistema operativo</strong><br>
L'agente è pronto per l'uso.
<div class="alert alert-success" id="system-status-alert">
<strong id="system-ok">✅ Sistema operativo</strong><br>
<span id="system-ready">L'agente è pronto per l'uso.</span>
</div>
<div class="config-item">
<div class="config-info">
<h3>Provider Attivo</h3>
<h3 id="active-provider-title">Provider Attivo</h3>
<p id="active-provider">Nessun provider configurato</p>
</div>
</div>
<div class="config-item">
<div class="config-info">
<h3>Modello Attivo</h3>
<h3 id="active-model-title">Modello Attivo</h3>
<p id="active-model">Nessun modello configurato</p>
</div>
</div>
<div class="config-item">
<div class="config-info">
<h3>Connessione</h3>
<h3 id="connection-title">Connessione</h3>
<p id="connection-status">Verifica in corso...</p>
</div>
<button class="btn btn-secondary" onclick="testConnection()">Testa Connessione</button>
<button class="btn btn-secondary" onclick="testConnection()" id="test-connection-btn">Testa Connessione</button>
</div>
</div>
</div>
@@ -372,8 +544,104 @@
// API base URL
const API_BASE = 'http://localhost:8000';
// Current language
let currentLanguage = 'it';
// Translations
const translations = {
it: {
title: "LangCode Agent",
subtitle: "Interfaccia di configurazione per l'agente AI",
tabConfig: "Configurazione",
tabChat: "Chat",
tabStatus: "Stato",
configTitle: "Gestione Configurazioni",
nameLabel: "Nome Configurazione",
providerLabel: "Provider",
modelLabel: "Nome Modello",
apiKeyLabel: "Chiave API",
baseUrlLabel: "URL Base",
setActiveLabel: "Imposta come configurazione attiva",
saveBtn: "Salva Configurazione",
clearBtn: "Pulisci",
existingConfigsTitle: "Configurazioni Esistenti",
chatTitle: "Chat con LangCode",
welcomeMessage: "Ciao! Sono LangCode, il tuo agente AI. Come posso aiutarti oggi?",
chatPlaceholder: "Scrivi un messaggio...",
sendBtn: "Invia",
statusTitle: "Stato del Sistema",
systemOk: "✅ Sistema operativo",
systemReady: "L'agente è pronto per l'uso.",
activeProviderTitle: "Provider Attivo",
activeModelTitle: "Modello Attivo",
connectionTitle: "Connessione",
testConnectionBtn: "Testa Connessione",
alertSuccess: "Configurazione salvata con successo!",
alertError: "Errore",
alertCancelled: "Annullato.",
alertTesting: "Test in corso...",
alertConnectionSuccess: "Connessione riuscita!",
alertConnectionFailed: "Connessione fallita",
noProvider: "Nessun provider configurato",
noModel: "Nessun modello configurato",
checking: "Verifica in corso...",
active: "Attivo",
inactive: "Inattivo",
test: "Testa",
delete: "Elimina",
activate: "Attiva"
},
en: {
title: "LangCode Agent",
subtitle: "Configuration interface for AI agent",
tabConfig: "Configuration",
tabChat: "Chat",
tabStatus: "Status",
configTitle: "Configuration Management",
nameLabel: "Configuration Name",
providerLabel: "Provider",
modelLabel: "Model Name",
apiKeyLabel: "API Key",
baseUrlLabel: "Base URL",
setActiveLabel: "Set as active configuration",
saveBtn: "Save Configuration",
clearBtn: "Clear",
existingConfigsTitle: "Existing Configurations",
chatTitle: "Chat with LangCode",
welcomeMessage: "Hello! I'm LangCode, your AI agent. How can I help you today?",
chatPlaceholder: "Type a message...",
sendBtn: "Send",
statusTitle: "System Status",
systemOk: "✅ System operational",
systemReady: "The agent is ready to use.",
activeProviderTitle: "Active Provider",
activeModelTitle: "Active Model",
connectionTitle: "Connection",
testConnectionBtn: "Test Connection",
alertSuccess: "Configuration saved successfully!",
alertError: "Error",
alertCancelled: "Cancelled.",
alertTesting: "Testing...",
alertConnectionSuccess: "Connection successful!",
alertConnectionFailed: "Connection failed",
noProvider: "No provider configured",
noModel: "No model configured",
checking: "Checking...",
active: "Active",
inactive: "Inactive",
test: "Test",
delete: "Delete",
activate: "Activate"
}
};
// Initialize
document.addEventListener('DOMContentLoaded', function() {
// Load saved language preference
const savedLanguage = localStorage.getItem('langcode-language') || 'it';
document.getElementById('language-select').value = savedLanguage;
changeLanguage(savedLanguage);
loadConfigurations();
loadActiveConfiguration();
@@ -396,6 +664,45 @@
});
});
// Change language
function changeLanguage(language) {
currentLanguage = language;
localStorage.setItem('langcode-language', language);
const t = translations[language];
// Update all translatable elements
document.getElementById('title').textContent = t.title;
document.getElementById('subtitle').textContent = t.subtitle;
document.getElementById('tab-config').textContent = t.tabConfig;
document.getElementById('tab-chat').textContent = t.tabChat;
document.getElementById('tab-status').textContent = t.tabStatus;
document.getElementById('config-title').textContent = t.configTitle;
document.getElementById('name-label').textContent = t.nameLabel;
document.getElementById('provider-label').textContent = t.providerLabel;
document.getElementById('model-label').textContent = t.modelLabel;
document.getElementById('api-key-label').textContent = t.apiKeyLabel;
document.getElementById('base-url-label').textContent = t.baseUrlLabel;
document.getElementById('set-active-label').textContent = t.setActiveLabel;
document.getElementById('save-btn').textContent = t.saveBtn;
document.getElementById('clear-btn').textContent = t.clearBtn;
document.getElementById('existing-configs-title').textContent = t.existingConfigsTitle;
document.getElementById('chat-title').textContent = t.chatTitle;
document.getElementById('welcome-message').textContent = t.welcomeMessage;
document.getElementById('chat-input').placeholder = t.chatPlaceholder;
document.getElementById('send-btn').textContent = t.sendBtn;
document.getElementById('status-title').textContent = t.statusTitle;
document.getElementById('system-ok').textContent = t.systemOk;
document.getElementById('system-ready').textContent = t.systemReady;
document.getElementById('active-provider-title').textContent = t.activeProviderTitle;
document.getElementById('active-model-title').textContent = t.activeModelTitle;
document.getElementById('connection-title').textContent = t.connectionTitle;
document.getElementById('test-connection-btn').textContent = t.testConnectionBtn;
// Update HTML lang attribute
document.documentElement.lang = language;
}
// Switch tabs
function switchTab(tabName) {
// Update tab buttons
@@ -421,7 +728,7 @@
container.innerHTML = '';
if (configs.length === 0) {
container.innerHTML = '<p>Nessuna configurazione trovata.</p>';
container.innerHTML = `<p>${translations[currentLanguage].noProvider}</p>`;
return;
}
@@ -431,20 +738,20 @@
item.innerHTML = `
<div class="config-info">
<h3>${config.name}
${config.is_active ? '<span class="status-badge status-active">Attivo</span>' : ''}
${config.is_active ? `<span class="status-badge status-active">${translations[currentLanguage].active}</span>` : ''}
</h3>
<p>Provider: ${config.provider} | Modello: ${config.model_name}</p>
<p>${translations[currentLanguage].providerLabel}: ${config.provider} | ${translations[currentLanguage].modelLabel}: ${config.model_name}</p>
</div>
<div class="config-actions">
<button class="btn btn-secondary" onclick="activateConfig('${config.name}')">Attiva</button>
<button class="btn btn-secondary" onclick="testConfig('${config.name}')">Testa</button>
<button class="btn btn-danger" onclick="deleteConfig('${config.name}')">Elimina</button>
<button class="btn btn-secondary" onclick="activateConfig('${config.name}')">${translations[currentLanguage].activate}</button>
<button class="btn btn-secondary" onclick="testConfig('${config.name}')">${translations[currentLanguage].test}</button>
<button class="btn btn-danger" onclick="deleteConfig('${config.name}')">${translations[currentLanguage].delete}</button>
</div>
`;
container.appendChild(item);
});
} catch (error) {
showAlert('Errore nel caricamento delle configurazioni: ' + error.message, 'error');
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
@@ -458,8 +765,8 @@
document.getElementById('active-provider').textContent = config.provider;
document.getElementById('active-model').textContent = config.model_name;
} else {
document.getElementById('active-provider').textContent = 'Nessun provider configurato';
document.getElementById('active-model').textContent = 'Nessun modello configurato';
document.getElementById('active-provider').textContent = translations[currentLanguage].noProvider;
document.getElementById('active-model').textContent = translations[currentLanguage].noModel;
}
} catch (error) {
console.error('Error loading active configuration:', error);
@@ -500,16 +807,16 @@
});
if (response.ok) {
showAlert('Configurazione salvata con successo!', 'success');
showAlert(translations[currentLanguage].alertSuccess, 'success');
clearForm();
loadConfigurations();
loadActiveConfiguration();
} else {
const error = await response.json();
showAlert('Errore: ' + error.detail, 'error');
showAlert(translations[currentLanguage].alertError + ': ' + error.detail, 'error');
}
} catch (error) {
showAlert('Errore di connessione: ' + error.message, 'error');
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
@@ -521,15 +828,15 @@
});
if (response.ok) {
showAlert(`Configurazione "${name}" attivata!`, 'success');
showAlert(`${translations[currentLanguage].alertSuccess} "${name}"`, 'success');
loadConfigurations();
loadActiveConfiguration();
} else {
const error = await response.json();
showAlert('Errore: ' + error.detail, 'error');
showAlert(translations[currentLanguage].alertError + ': ' + error.detail, 'error');
}
} catch (error) {
showAlert('Errore di connessione: ' + error.message, 'error');
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
@@ -548,7 +855,7 @@
showAlert(`❌ ${result.message}`, 'error');
}
} catch (error) {
showAlert('Errore di connessione: ' + error.message, 'error');
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
@@ -564,36 +871,36 @@
});
if (response.ok) {
showAlert(`Configurazione "${name}" eliminata!`, 'success');
showAlert(`${translations[currentLanguage].alertSuccess} "${name}"`, 'success');
loadConfigurations();
loadActiveConfiguration();
} else {
const error = await response.json();
showAlert('Errore: ' + error.detail, 'error');
showAlert(translations[currentLanguage].alertError + ': ' + error.detail, 'error');
}
} catch (error) {
showAlert('Errore di connessione: ' + error.message, 'error');
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
// Test connection
async function testConnection() {
document.getElementById('connection-status').textContent = 'Test in corso...';
document.getElementById('connection-status').textContent = translations[currentLanguage].alertTesting;
try {
const response = await fetch(API_BASE + '/health');
const result = await response.json();
if (result.status === 'healthy') {
document.getElementById('connection-status').textContent = '✅ Connessione riuscita';
showAlert('Connessione al server riuscita!', 'success');
document.getElementById('connection-status').textContent = translations[currentLanguage].alertConnectionSuccess;
showAlert(translations[currentLanguage].alertConnectionSuccess, 'success');
} else {
document.getElementById('connection-status').textContent = '❌ Connessione fallita';
showAlert('Connessione al server fallita', 'error');
document.getElementById('connection-status').textContent = translations[currentLanguage].alertConnectionFailed;
showAlert(translations[currentLanguage].alertConnectionFailed, 'error');
}
} catch (error) {
document.getElementById('connection-status').textContent = '❌ Errore di connessione';
showAlert('Errore di connessione: ' + error.message, 'error');
document.getElementById('connection-status').textContent = translations[currentLanguage].alertConnectionFailed;
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
@@ -622,7 +929,7 @@
// Add agent response to chat
addMessageToChat(result.response, 'agent');
} catch (error) {
addMessageToChat('Errore: Impossibile connettersi al server', 'agent');
addMessageToChat(translations[currentLanguage].alertError + ': ' + error.message, 'agent');
}
}
@@ -644,7 +951,7 @@
const baseUrlInput = document.getElementById('config-base-url');
// Show/hide fields based on provider
if (provider === 'openai' || provider === 'openrouter') {
if (provider === 'openai' || provider === 'openrouter' || provider === 'anthropic' || provider === 'google') {
apiKeyGroup.style.display = 'block';
} else {
apiKeyGroup.style.display = 'none';
+906
View File
@@ -0,0 +1,906 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LangCode Agent - Marketplace</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}
header {
text-align: center;
color: white;
margin-bottom: 30px;
}
header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
}
header p {
font-size: 1.1rem;
opacity: 0.9;
}
.language-selector {
position: absolute;
top: 20px;
right: 20px;
}
.language-selector select {
padding: 8px 12px;
border-radius: 6px;
border: 1px solid #ddd;
background: white;
font-size: 14px;
cursor: pointer;
}
.card {
background: white;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
padding: 30px;
margin-bottom: 30px;
}
.card h2 {
color: #333;
margin-bottom: 20px;
border-bottom: 2px solid #667eea;
padding-bottom: 10px;
}
.search-container {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.search-input {
flex: 1;
min-width: 200px;
padding: 12px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 16px;
}
.search-btn {
background: #667eea;
color: white;
border: none;
padding: 12px 24px;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
font-weight: 600;
transition: background 0.3s;
}
.search-btn:hover {
background: #5a6fd8;
}
.stats-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 6px;
padding: 20px;
text-align: center;
}
.stat-value {
font-size: 2rem;
font-weight: 700;
color: #667eea;
margin-bottom: 5px;
}
.stat-label {
font-size: 0.9rem;
color: #666;
}
.plugins-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
}
.plugin-card {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 6px;
padding: 20px;
transition: transform 0.2s, box-shadow 0.2s;
}
.plugin-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.plugin-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 10px;
}
.plugin-name {
font-size: 1.2rem;
font-weight: 600;
color: #333;
}
.plugin-status {
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
.status-installed {
background: #d4edda;
color: #155724;
}
.status-available {
background: #fff3cd;
color: #856404;
}
.plugin-description {
color: #666;
font-size: 14px;
margin-bottom: 15px;
line-height: 1.4;
}
.plugin-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.btn {
padding: 8px 16px;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: background 0.3s;
border: none;
}
.btn-primary {
background: #667eea;
color: white;
}
.btn-primary:hover {
background: #5a6fd8;
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn-secondary:hover {
background: #5a6268;
}
.btn-danger {
background: #dc3545;
color: white;
}
.btn-danger:hover {
background: #c82333;
}
.btn-success {
background: #28a745;
color: white;
}
.btn-success:hover {
background: #218838;
}
.alert {
padding: 15px;
border-radius: 6px;
margin-bottom: 20px;
}
.alert-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.alert-warning {
background: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.tabs {
display: flex;
border-bottom: 2px solid #dee2e6;
margin-bottom: 20px;
flex-wrap: wrap;
}
.tab {
padding: 10px 20px;
cursor: pointer;
border-bottom: 3px solid transparent;
margin-bottom: -2px;
flex: 1;
text-align: center;
min-width: 120px;
}
.tab.active {
border-bottom-color: #667eea;
font-weight: 600;
color: #667eea;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.hidden {
display: none;
}
/* Responsive Design */
@media (max-width: 768px) {
body {
padding: 10px;
}
.container {
padding: 0 10px;
}
header h1 {
font-size: 2rem;
}
header p {
font-size: 1rem;
}
.card {
padding: 20px;
margin-bottom: 20px;
}
.card h2 {
font-size: 1.3rem;
}
.search-container {
flex-direction: column;
}
.search-input {
min-width: 100%;
}
.search-btn {
width: 100%;
}
.stats-container {
grid-template-columns: 1fr;
}
.plugins-grid {
grid-template-columns: 1fr;
}
.plugin-card {
padding: 15px;
}
.plugin-actions {
flex-direction: column;
}
.btn {
width: 100%;
margin: 5px 0;
}
.tabs {
flex-direction: column;
}
.tab {
padding: 8px 15px;
border-bottom: none;
border-left: 3px solid transparent;
margin-bottom: 0;
text-align: left;
}
.tab.active {
border-bottom: none;
border-left-color: #667eea;
}
.language-selector {
position: static;
text-align: center;
margin-bottom: 20px;
}
}
@media (max-width: 480px) {
header h1 {
font-size: 1.8rem;
}
.card {
padding: 15px;
}
.stat-value {
font-size: 1.5rem;
}
}
/* Touch-friendly improvements */
@media (hover: none) and (pointer: coarse) {
.btn:hover {
background: inherit;
}
.btn:active {
transform: scale(0.98);
}
.plugin-card:hover {
transform: none;
}
.plugin-card:active {
transform: scale(0.98);
}
}
</style>
</head>
<body>
<div class="container">
<div class="language-selector">
<select id="language-select" onchange="changeLanguage(this.value)">
<option value="it">Italiano</option>
<option value="en">English</option>
</select>
</div>
<header>
<h1 id="title">LangCode Agent - Marketplace</h1>
<p id="subtitle">Scopri e installa plugin per estendere le funzionalità</p>
</header>
<div class="card">
<div class="tabs">
<div class="tab active" onclick="switchTab('marketplace')" id="tab-marketplace">Marketplace</div>
<div class="tab" onclick="switchTab('installed')" id="tab-installed">Plugin Installati</div>
<div class="tab" onclick="switchTab('search')" id="tab-search">Cerca</div>
</div>
<div id="marketplace-tab" class="tab-content active">
<h2 id="marketplace-title">🏪 Plugin Disponibili</h2>
<div id="alert-container"></div>
<div class="stats-container">
<div class="stat-card">
<div class="stat-value" id="total-plugins">0</div>
<div class="stat-label" id="total-plugins-label">Plugin Totali</div>
</div>
<div class="stat-card">
<div class="stat-value" id="installed-plugins">0</div>
<div class="stat-label" id="installed-plugins-label">Plugin Installati</div>
</div>
<div class="stat-card">
<div class="stat-value" id="available-plugins">0</div>
<div class="stat-label" id="available-plugins-label">Plugin Disponibili</div>
</div>
</div>
<div class="plugins-grid" id="plugins-grid">
<!-- Plugins will be loaded here -->
</div>
</div>
<div id="installed-tab" class="tab-content">
<h2 id="installed-title">📦 Plugin Installati</h2>
<div class="plugins-grid" id="installed-plugins-grid">
<!-- Installed plugins will be loaded here -->
</div>
</div>
<div id="search-tab" class="tab-content">
<h2 id="search-title">🔍 Cerca Plugin</h2>
<div class="search-container">
<input type="text" class="search-input" id="search-input" placeholder="Cerca plugin...">
<button class="search-btn" onclick="searchPlugins()" id="search-btn">Cerca</button>
</div>
<div class="plugins-grid" id="search-results-grid">
<!-- Search results will be loaded here -->
</div>
</div>
</div>
</div>
<script>
// API base URL
const API_BASE = 'http://localhost:8000';
// Current language
let currentLanguage = 'it';
// Translations
const translations = {
it: {
title: "LangCode Agent - Marketplace",
subtitle: "Scopri e installa plugin per estendere le funzionalità",
tabMarketplace: "Marketplace",
tabInstalled: "Plugin Installati",
tabSearch: "Cerca",
marketplaceTitle: "🏪 Plugin Disponibili",
totalPluginsLabel: "Plugin Totali",
installedPluginsLabel: "Plugin Installati",
availablePluginsLabel: "Plugin Disponibili",
installedTitle: "📦 Plugin Installati",
searchTitle: "🔍 Cerca Plugin",
searchPlaceholder: "Cerca plugin...",
searchBtn: "Cerca",
installBtn: "Installa",
uninstallBtn: "Disinstalla",
infoBtn: "Info",
alertSuccess: "Operazione completata con successo!",
alertError: "Errore",
alertWarning: "Avviso",
alertSearching: "Ricerca in corso...",
alertInstalling: "Installazione in corso...",
alertUninstalling: "Disinstallazione in corso...",
noPlugins: "Nessun plugin trovato",
installed: "Installato",
available: "Disponibile",
refreshBtn: "Aggiorna"
},
en: {
title: "LangCode Agent - Marketplace",
subtitle: "Discover and install plugins to extend functionality",
tabMarketplace: "Marketplace",
tabInstalled: "Installed Plugins",
tabSearch: "Search",
marketplaceTitle: "🏪 Available Plugins",
totalPluginsLabel: "Total Plugins",
installedPluginsLabel: "Installed Plugins",
availablePluginsLabel: "Available Plugins",
installedTitle: "📦 Installed Plugins",
searchTitle: "🔍 Search Plugins",
searchPlaceholder: "Search plugins...",
searchBtn: "Search",
installBtn: "Install",
uninstallBtn: "Uninstall",
infoBtn: "Info",
alertSuccess: "Operation completed successfully!",
alertError: "Error",
alertWarning: "Warning",
alertSearching: "Searching...",
alertInstalling: "Installing...",
alertUninstalling: "Uninstalling...",
noPlugins: "No plugins found",
installed: "Installed",
available: "Available",
refreshBtn: "Refresh"
}
};
// Initialize
document.addEventListener('DOMContentLoaded', function() {
// Load saved language preference
const savedLanguage = localStorage.getItem('langcode-language') || 'it';
document.getElementById('language-select').value = savedLanguage;
changeLanguage(savedLanguage);
loadMarketplaceStats();
loadPlugins();
loadInstalledPlugins();
// Handle search input
document.getElementById('search-input').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
searchPlugins();
}
});
});
// Change language
function changeLanguage(language) {
currentLanguage = language;
localStorage.setItem('langcode-language', language);
const t = translations[language];
// Update all translatable elements
document.getElementById('title').textContent = t.title;
document.getElementById('subtitle').textContent = t.subtitle;
document.getElementById('tab-marketplace').textContent = t.tabMarketplace;
document.getElementById('tab-installed').textContent = t.tabInstalled;
document.getElementById('tab-search').textContent = t.tabSearch;
document.getElementById('marketplace-title').textContent = t.marketplaceTitle;
document.getElementById('total-plugins-label').textContent = t.totalPluginsLabel;
document.getElementById('installed-plugins-label').textContent = t.installedPluginsLabel;
document.getElementById('available-plugins-label').textContent = t.availablePluginsLabel;
document.getElementById('installed-title').textContent = t.installedTitle;
document.getElementById('search-title').textContent = t.searchTitle;
document.getElementById('search-input').placeholder = t.searchPlaceholder;
document.getElementById('search-btn').textContent = t.searchBtn;
// Update HTML lang attribute
document.documentElement.lang = language;
}
// Switch tabs
function switchTab(tabName) {
// Update tab buttons
document.querySelectorAll('.tab').forEach(tab => {
tab.classList.remove('active');
});
event.target.classList.add('active');
// Update tab content
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
document.getElementById(tabName + '-tab').classList.add('active');
}
// Load marketplace stats
async function loadMarketplaceStats() {
try {
// Simulate stats (in real implementation, this would come from API)
const stats = {
total_plugins: 15,
installed_plugins: 3,
available_plugins: 12
};
document.getElementById('total-plugins').textContent = stats.total_plugins;
document.getElementById('installed-plugins').textContent = stats.installed_plugins;
document.getElementById('available-plugins').textContent = stats.available_plugins;
} catch (error) {
console.error('Error loading marketplace stats:', error);
}
}
// Load plugins
async function loadPlugins() {
try {
// Simulate plugins (in real implementation, this would come from API)
const plugins = [
{
name: "weather",
description: "Get weather information for any location",
installed: false,
downloads: 1250,
rating: 4.5
},
{
name: "calendar",
description: "Manage calendar events and reminders",
installed: true,
downloads: 980,
rating: 4.2
},
{
name: "translation",
description: "Translate text between languages",
installed: false,
downloads: 2100,
rating: 4.7
},
{
name: "image_recognition",
description: "Analyze and describe images",
installed: false,
downloads: 750,
rating: 4.0
},
{
name: "code_analysis",
description: "Analyze code quality and suggest improvements",
installed: true,
downloads: 1500,
rating: 4.8
},
{
name: "database_tools",
description: "Connect to and query databases",
installed: false,
downloads: 620,
rating: 4.3
}
];
const container = document.getElementById('plugins-grid');
container.innerHTML = '';
plugins.forEach(plugin => {
const card = document.createElement('div');
card.className = 'plugin-card';
const statusClass = plugin.installed ? 'status-installed' : 'status-available';
const statusText = plugin.installed ? translations[currentLanguage].installed : translations[currentLanguage].available;
card.innerHTML = `
<div class="plugin-header">
<div class="plugin-name">${plugin.name}</div>
<div class="plugin-status ${statusClass}">${statusText}</div>
</div>
<div class="plugin-description">${plugin.description}</div>
<div class="plugin-actions">
${plugin.installed ?
`<button class="btn btn-danger" onclick="uninstallPlugin('${plugin.name}')">${translations[currentLanguage].uninstallBtn}</button>` :
`<button class="btn btn-primary" onclick="installPlugin('${plugin.name}')">${translations[currentLanguage].installBtn}</button>`
}
<button class="btn btn-secondary" onclick="showPluginInfo('${plugin.name}')">${translations[currentLanguage].infoBtn}</button>
</div>
`;
container.appendChild(card);
});
} catch (error) {
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
// Load installed plugins
async function loadInstalledPlugins() {
try {
// Simulate installed plugins (in real implementation, this would come from API)
const installed = [
{
name: "calendar",
description: "Manage calendar events and reminders",
version: "1.0.0",
author: "LangCode Team"
},
{
name: "code_analysis",
description: "Analyze code quality and suggest improvements",
version: "1.2.0",
author: "Community"
},
{
name: "file_tools",
description: "Additional file management tools",
version: "1.0.0",
author: "LangCode Team"
}
];
const container = document.getElementById('installed-plugins-grid');
container.innerHTML = '';
if (installed.length === 0) {
container.innerHTML = `<p>${translations[currentLanguage].noPlugins}</p>`;
return;
}
installed.forEach(plugin => {
const card = document.createElement('div');
card.className = 'plugin-card';
card.innerHTML = `
<div class="plugin-header">
<div class="plugin-name">${plugin.name}</div>
<div class="plugin-status status-installed">${translations[currentLanguage].installed}</div>
</div>
<div class="plugin-description">${plugin.description}</div>
<div class="plugin-actions">
<button class="btn btn-danger" onclick="uninstallPlugin('${plugin.name}')">${translations[currentLanguage].uninstallBtn}</button>
<button class="btn btn-secondary" onclick="showPluginInfo('${plugin.name}')">${translations[currentLanguage].infoBtn}</button>
</div>
`;
container.appendChild(card);
});
} catch (error) {
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
// Search plugins
async function searchPlugins() {
const query = document.getElementById('search-input').value.trim();
if (!query) {
showAlert(translations[currentLanguage].alertWarning + ': Inserisci un termine di ricerca', 'warning');
return;
}
showAlert(translations[currentLanguage].alertSearching, 'info');
try {
// Simulate search results (in real implementation, this would come from API)
const results = [
{
name: "weather",
description: "Get weather information for any location",
installed: false
},
{
name: "translation",
description: "Translate text between languages",
installed: false
}
];
const container = document.getElementById('search-results-grid');
container.innerHTML = '';
if (results.length === 0) {
container.innerHTML = `<p>${translations[currentLanguage].noPlugins}</p>`;
return;
}
results.forEach(plugin => {
const card = document.createElement('div');
card.className = 'plugin-card';
const statusClass = plugin.installed ? 'status-installed' : 'status-available';
const statusText = plugin.installed ? translations[currentLanguage].installed : translations[currentLanguage].available;
card.innerHTML = `
<div class="plugin-header">
<div class="plugin-name">${plugin.name}</div>
<div class="plugin-status ${statusClass}">${statusText}</div>
</div>
<div class="plugin-description">${plugin.description}</div>
<div class="plugin-actions">
${plugin.installed ?
`<button class="btn btn-danger" onclick="uninstallPlugin('${plugin.name}')">${translations[currentLanguage].uninstallBtn}</button>` :
`<button class="btn btn-primary" onclick="installPlugin('${plugin.name}')">${translations[currentLanguage].installBtn}</button>`
}
<button class="btn btn-secondary" onclick="showPluginInfo('${plugin.name}')">${translations[currentLanguage].infoBtn}</button>
</div>
`;
container.appendChild(card);
});
showAlert(`${translations[currentLanguage].alertSuccess} Trovati ${results.length} plugin`, 'success');
} catch (error) {
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
// Install plugin
async function installPlugin(pluginName) {
showAlert(translations[currentLanguage].alertInstalling + ' ' + pluginName, 'info');
try {
// Simulate installation (in real implementation, this would call API)
await new Promise(resolve => setTimeout(resolve, 1500));
showAlert(`${translations[currentLanguage].alertSuccess} Plugin '${pluginName}' installato!`, 'success');
// Refresh the lists
loadPlugins();
loadInstalledPlugins();
loadMarketplaceStats();
} catch (error) {
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
// Uninstall plugin
async function uninstallPlugin(pluginName) {
if (!confirm(`Sei sicuro di voler disinstallare il plugin '${pluginName}'?`)) {
return;
}
showAlert(translations[currentLanguage].alertUninstalling + ' ' + pluginName, 'info');
try {
// Simulate uninstallation (in real implementation, this would call API)
await new Promise(resolve => setTimeout(resolve, 1000));
showAlert(`${translations[currentLanguage].alertSuccess} Plugin '${pluginName}' disinstallato!`, 'success');
// Refresh the lists
loadPlugins();
loadInstalledPlugins();
loadMarketplaceStats();
} catch (error) {
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'error');
}
}
// Show plugin info
function showPluginInfo(pluginName) {
alert(`Informazioni sul plugin: ${pluginName}\n\nQuesta funzionalità mostrerà dettagli completi sul plugin, inclusi:\n- Versione\n- Autore\n- Descrizione dettagliata\n- Dipendenze\n- Documentazione`);
}
// Show alert
function showAlert(message, type) {
const container = document.getElementById('alert-container');
const alert = document.createElement('div');
alert.className = `alert alert-${type}`;
alert.textContent = message;
// Remove existing alerts
container.innerHTML = '';
container.appendChild(alert);
// Auto-remove after 5 seconds
setTimeout(() => {
if (alert.parentNode) {
alert.parentNode.removeChild(alert);
}
}, 5000);
}
</script>
</body>
</html>
+829
View File
@@ -0,0 +1,829 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LangCode Agent - Monitoring</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
header {
text-align: center;
color: white;
margin-bottom: 30px;
}
header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
}
header p {
font-size: 1.1rem;
opacity: 0.9;
}
.language-selector {
position: absolute;
top: 20px;
right: 20px;
}
.language-selector select {
padding: 8px 12px;
border-radius: 6px;
border: 1px solid #ddd;
background: white;
font-size: 14px;
cursor: pointer;
}
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.card {
background: white;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
padding: 20px;
}
.card h2 {
color: #333;
margin-bottom: 15px;
border-bottom: 2px solid #667eea;
padding-bottom: 10px;
font-size: 1.2rem;
}
.metric {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.metric:last-child {
border-bottom: none;
}
.metric-label {
font-weight: 600;
color: #555;
}
.metric-value {
font-size: 1.2rem;
font-weight: 700;
color: #667eea;
}
.metric-value.success {
color: #28a745;
}
.metric-value.warning {
color: #ffc107;
}
.metric-value.danger {
color: #dc3545;
}
.progress-bar {
width: 100%;
height: 20px;
background: #e9ecef;
border-radius: 10px;
overflow: hidden;
margin: 10px 0;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #667eea, #764ba2);
transition: width 0.5s ease;
}
.chart-container {
height: 200px;
margin-top: 15px;
}
.status-indicator {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 8px;
}
.status-online {
background: #28a745;
box-shadow: 0 0 8px #28a745;
}
.status-offline {
background: #dc3545;
box-shadow: 0 0 8px #dc3545;
}
.status-warning {
background: #ffc107;
box-shadow: 0 0 8px #ffc107;
}
.refresh-btn {
background: #667eea;
color: white;
border: none;
padding: 10px 20px;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
font-weight: 600;
transition: background 0.3s;
margin-bottom: 20px;
}
.refresh-btn:hover {
background: #5a6fd8;
}
.last-updated {
text-align: right;
color: #666;
font-size: 0.9rem;
margin-top: 10px;
}
.alert {
padding: 15px;
border-radius: 6px;
margin-bottom: 20px;
}
.alert-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-warning {
background: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.alert-danger {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.log-container {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 6px;
padding: 15px;
height: 300px;
overflow-y: auto;
font-family: 'Courier New', monospace;
font-size: 14px;
line-height: 1.4;
}
.log-entry {
margin-bottom: 5px;
padding: 5px;
border-radius: 3px;
}
.log-info {
background: #d1ecf1;
color: #0c5460;
}
.log-warning {
background: #fff3cd;
color: #856404;
}
.log-error {
background: #f8d7da;
color: #721c24;
}
.log-success {
background: #d4edda;
color: #155724;
}
.tabs {
display: flex;
border-bottom: 2px solid #dee2e6;
margin-bottom: 20px;
}
.tab {
padding: 10px 20px;
cursor: pointer;
border-bottom: 3px solid transparent;
margin-bottom: -2px;
}
.tab.active {
border-bottom-color: #667eea;
font-weight: 600;
color: #667eea;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.hidden {
display: none;
}
@media (max-width: 768px) {
.dashboard {
grid-template-columns: 1fr;
}
header h1 {
font-size: 2rem;
}
.language-selector {
position: static;
text-align: center;
margin-bottom: 20px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="language-selector">
<select id="language-select" onchange="changeLanguage(this.value)">
<option value="it">Italiano</option>
<option value="en">English</option>
</select>
</div>
<header>
<h1 id="title">LangCode Agent - Monitoraggio</h1>
<p id="subtitle">Dashboard di monitoraggio in tempo reale</p>
</header>
<button class="refresh-btn" onclick="refreshData()" id="refresh-btn">🔄 Aggiorna Dati</button>
<div id="alert-container"></div>
<div class="tabs">
<div class="tab active" onclick="switchTab('dashboard')" id="tab-dashboard">Dashboard</div>
<div class="tab" onclick="switchTab('logs')" id="tab-logs">Logs</div>
<div class="tab" onclick="switchTab('performance')" id="tab-performance">Performance</div>
<div class="tab" onclick="switchTab('system')" id="tab-system">Sistema</div>
</div>
<div id="dashboard-tab" class="tab-content active">
<div class="dashboard">
<div class="card">
<h2 id="stats-title">📊 Statistiche Generali</h2>
<div class="metric">
<span class="metric-label" id="status-label">Stato Sistema</span>
<span class="metric-value success" id="system-status">
<span class="status-indicator status-online"></span><span id="status-online">Online</span>
</span>
</div>
<div class="metric">
<span class="metric-label" id="uptime-label">Uptime</span>
<span class="metric-value" id="system-uptime">0 giorni, 0 ore</span>
</div>
<div class="metric">
<span class="metric-label" id="requests-label">Richieste Totali</span>
<span class="metric-value" id="total-requests">0</span>
</div>
<div class="metric">
<span class="metric-label" id="errors-label">Errori Totali</span>
<span class="metric-value" id="total-errors">0</span>
</div>
<div class="metric">
<span class="metric-label" id="error-rate-label">Tasso di Errore</span>
<span class="metric-value" id="error-rate">0%</span>
</div>
</div>
<div class="card">
<h2 id="provider-title">🤖 Provider Attivo</h2>
<div class="metric">
<span class="metric-label" id="provider-label">Provider</span>
<span class="metric-value" id="active-provider">Nessuno</span>
</div>
<div class="metric">
<span class="metric-label" id="model-label">Modello</span>
<span class="metric-value" id="active-model">Nessuno</span>
</div>
<div class="metric">
<span class="metric-label" id="connection-label">Stato Connessione</span>
<span class="metric-value" id="connection-status">
<span class="status-indicator status-offline"></span><span id="status-offline">Non connesso</span>
</span>
</div>
<div class="metric">
<span class="metric-label" id="calls-label">Chiamate Provider</span>
<span class="metric-value" id="provider-calls">0</span>
</div>
</div>
<div class="card">
<h2 id="tools-title">🔧 Utilizzo Strumenti</h2>
<div id="tools-usage">
<div class="metric">
<span class="metric-label" id="tools-loaded-label">Strumenti Caricati</span>
<span class="metric-value" id="tools-loaded">0</span>
</div>
<div class="metric">
<span class="metric-label" id="plugins-label">Plugin Attivi</span>
<span class="metric-value" id="plugins-active">0</span>
</div>
</div>
</div>
<div class="card">
<h2 id="resources-title">💻 Risorse di Sistema</h2>
<div class="metric">
<span class="metric-label" id="cpu-label">CPU</span>
<span class="metric-value" id="cpu-usage">0%</span>
</div>
<div class="progress-bar">
<div class="progress-fill" id="cpu-bar" style="width: 0%"></div>
</div>
<div class="metric">
<span class="metric-label" id="memory-label">Memoria</span>
<span class="metric-value" id="memory-usage">0%</span>
</div>
<div class="progress-bar">
<div class="progress-fill" id="memory-bar" style="width: 0%"></div>
</div>
<div class="metric">
<span class="metric-label" id="disk-label">Disco</span>
<span class="metric-value" id="disk-usage">0%</span>
</div>
<div class="progress-bar">
<div class="progress-fill" id="disk-bar" style="width: 0%"></div>
</div>
</div>
</div>
</div>
<div id="logs-tab" class="tab-content">
<div class="card">
<h2 id="logs-title">📋 Logs Recenti</h2>
<div class="log-container" id="logs-container">
<!-- Logs will be loaded here -->
</div>
</div>
</div>
<div id="performance-tab" class="tab-content">
<div class="card">
<h2 id="performance-title">📈 Performance</h2>
<div class="metric">
<span class="metric-label" id="response-time-label">Tempo di Risposta Medio</span>
<span class="metric-value" id="avg-response-time">0s</span>
</div>
<div class="metric">
<span class="metric-label" id="rpm-label">Richieste al Minuto</span>
<span class="metric-value" id="requests-per-minute">0</span>
</div>
<div class="metric">
<span class="metric-label" id="peak-label">Picco Richieste</span>
<span class="metric-value" id="peak-requests">0</span>
</div>
<div class="chart-container" id="performance-chart">
<!-- Chart will be rendered here -->
</div>
</div>
</div>
<div id="system-tab" class="tab-content">
<div class="card">
<h2 id="system-info-title">🖥️ Informazioni di Sistema</h2>
<div class="metric">
<span class="metric-label" id="hostname-label">Hostname</span>
<span class="metric-value" id="system-hostname">-</span>
</div>
<div class="metric">
<span class="metric-label" id="platform-label">Piattaforma</span>
<span class="metric-value" id="system-platform">-</span>
</div>
<div class="metric">
<span class="metric-label" id="architecture-label">Architettura</span>
<span class="metric-value" id="system-architecture">-</span>
</div>
<div class="metric">
<span class="metric-label" id="python-label">Python Version</span>
<span class="metric-value" id="python-version">-</span>
</div>
<div class="metric">
<span class="metric-label" id="langcode-label">LangCode Version</span>
<span class="metric-value" id="langcode-version">1.0.0</span>
</div>
</div>
</div>
<div class="last-updated">
<span id="last-updated-label">Ultimo aggiornamento:</span> <span id="last-updated">-</span>
</div>
</div>
<script>
// API base URL
const API_BASE = 'http://localhost:8000';
// Auto-refresh interval (in milliseconds)
const REFRESH_INTERVAL = 30000; // 30 seconds
// Current language
let currentLanguage = 'it';
// Translations
const translations = {
it: {
title: "LangCode Agent - Monitoraggio",
subtitle: "Dashboard di monitoraggio in tempo reale",
refreshBtn: "🔄 Aggiorna Dati",
tabDashboard: "Dashboard",
tabLogs: "Logs",
tabPerformance: "Performance",
tabSystem: "Sistema",
statsTitle: "📊 Statistiche Generali",
statusLabel: "Stato Sistema",
statusOnline: "Online",
statusOffline: "Non connesso",
uptimeLabel: "Uptime",
requestsLabel: "Richieste Totali",
errorsLabel: "Errori Totali",
errorRateLabel: "Tasso di Errore",
providerTitle: "🤖 Provider Attivo",
providerLabel: "Provider",
modelLabel: "Modello",
connectionLabel: "Stato Connessione",
callsLabel: "Chiamate Provider",
toolsTitle: "🔧 Utilizzo Strumenti",
toolsLoadedLabel: "Strumenti Caricati",
pluginsLabel: "Plugin Attivi",
resourcesTitle: "💻 Risorse di Sistema",
cpuLabel: "CPU",
memoryLabel: "Memoria",
diskLabel: "Disco",
logsTitle: "📋 Logs Recenti",
performanceTitle: "📈 Performance",
responseTimeLabel: "Tempo di Risposta Medio",
rpmLabel: "Richieste al Minuto",
peakLabel: "Picco Richieste",
systemInfoTitle: "🖥️ Informazioni di Sistema",
hostnameLabel: "Hostname",
platformLabel: "Piattaforma",
architectureLabel: "Architettura",
pythonLabel: "Python Version",
langcodeLabel: "LangCode Version",
lastUpdatedLabel: "Ultimo aggiornamento:",
alertSuccess: "Dati aggiornati con successo",
alertError: "Errore nel caricamento dei dati"
},
en: {
title: "LangCode Agent - Monitoring",
subtitle: "Real-time monitoring dashboard",
refreshBtn: "🔄 Refresh Data",
tabDashboard: "Dashboard",
tabLogs: "Logs",
tabPerformance: "Performance",
tabSystem: "System",
statsTitle: "📊 General Statistics",
statusLabel: "System Status",
statusOnline: "Online",
statusOffline: "Offline",
uptimeLabel: "Uptime",
requestsLabel: "Total Requests",
errorsLabel: "Total Errors",
errorRateLabel: "Error Rate",
providerTitle: "🤖 Active Provider",
providerLabel: "Provider",
modelLabel: "Model",
connectionLabel: "Connection Status",
callsLabel: "Provider Calls",
toolsTitle: "🔧 Tool Usage",
toolsLoadedLabel: "Loaded Tools",
pluginsLabel: "Active Plugins",
resourcesTitle: "💻 System Resources",
cpuLabel: "CPU",
memoryLabel: "Memory",
diskLabel: "Disk",
logsTitle: "📋 Recent Logs",
performanceTitle: "📈 Performance",
responseTimeLabel: "Average Response Time",
rpmLabel: "Requests per Minute",
peakLabel: "Peak Requests",
systemInfoTitle: "🖥️ System Information",
hostnameLabel: "Hostname",
platformLabel: "Platform",
architectureLabel: "Architecture",
pythonLabel: "Python Version",
langcodeLabel: "LangCode Version",
lastUpdatedLabel: "Last updated:",
alertSuccess: "Data refreshed successfully",
alertError: "Error loading data"
}
};
// Initialize
document.addEventListener('DOMContentLoaded', function() {
// Load saved language preference
const savedLanguage = localStorage.getItem('langcode-language') || 'it';
document.getElementById('language-select').value = savedLanguage;
changeLanguage(savedLanguage);
refreshData();
// Set up auto-refresh
setInterval(refreshData, REFRESH_INTERVAL);
});
// Change language
function changeLanguage(language) {
currentLanguage = language;
localStorage.setItem('langcode-language', language);
const t = translations[language];
// Update all translatable elements
document.getElementById('title').textContent = t.title;
document.getElementById('subtitle').textContent = t.subtitle;
document.getElementById('refresh-btn').textContent = t.refreshBtn;
document.getElementById('tab-dashboard').textContent = t.tabDashboard;
document.getElementById('tab-logs').textContent = t.tabLogs;
document.getElementById('tab-performance').textContent = t.tabPerformance;
document.getElementById('tab-system').textContent = t.tabSystem;
document.getElementById('stats-title').textContent = t.statsTitle;
document.getElementById('status-label').textContent = t.statusLabel;
document.getElementById('status-online').textContent = t.statusOnline;
document.getElementById('status-offline').textContent = t.statusOffline;
document.getElementById('uptime-label').textContent = t.uptimeLabel;
document.getElementById('requests-label').textContent = t.requestsLabel;
document.getElementById('errors-label').textContent = t.errorsLabel;
document.getElementById('error-rate-label').textContent = t.errorRateLabel;
document.getElementById('provider-title').textContent = t.providerTitle;
document.getElementById('provider-label').textContent = t.providerLabel;
document.getElementById('model-label').textContent = t.modelLabel;
document.getElementById('connection-label').textContent = t.connectionLabel;
document.getElementById('calls-label').textContent = t.callsLabel;
document.getElementById('tools-title').textContent = t.toolsTitle;
document.getElementById('tools-loaded-label').textContent = t.toolsLoadedLabel;
document.getElementById('plugins-label').textContent = t.pluginsLabel;
document.getElementById('resources-title').textContent = t.resourcesTitle;
document.getElementById('cpu-label').textContent = t.cpuLabel;
document.getElementById('memory-label').textContent = t.memoryLabel;
document.getElementById('disk-label').textContent = t.diskLabel;
document.getElementById('logs-title').textContent = t.logsTitle;
document.getElementById('performance-title').textContent = t.performanceTitle;
document.getElementById('response-time-label').textContent = t.responseTimeLabel;
document.getElementById('rpm-label').textContent = t.rpmLabel;
document.getElementById('peak-label').textContent = t.peakLabel;
document.getElementById('system-info-title').textContent = t.systemInfoTitle;
document.getElementById('hostname-label').textContent = t.hostnameLabel;
document.getElementById('platform-label').textContent = t.platformLabel;
document.getElementById('architecture-label').textContent = t.architectureLabel;
document.getElementById('python-label').textContent = t.pythonLabel;
document.getElementById('langcode-label').textContent = t.langcodeLabel;
document.getElementById('last-updated-label').textContent = t.lastUpdatedLabel;
// Update HTML lang attribute
document.documentElement.lang = language;
}
// Switch tabs
function switchTab(tabName) {
// Update tab buttons
document.querySelectorAll('.tab').forEach(tab => {
tab.classList.remove('active');
});
event.target.classList.add('active');
// Update tab content
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
document.getElementById(tabName + '-tab').classList.add('active');
}
// Refresh data
async function refreshData() {
try {
// Fetch health status
const healthResponse = await fetch(API_BASE + '/health');
const healthData = await healthResponse.json();
// Fetch configurations
const configResponse = await fetch(API_BASE + '/configurations');
const configs = await configResponse.json();
// Fetch active configuration
const activeResponse = await fetch(API_BASE + '/configurations/active');
const activeConfig = await activeResponse.json();
// Update UI
updateDashboard(healthData, configs, activeConfig);
updateLastUpdated();
// Show success alert
showAlert(translations[currentLanguage].alertSuccess, 'success');
} catch (error) {
console.error('Error refreshing data:', error);
showAlert(translations[currentLanguage].alertError + ': ' + error.message, 'danger');
}
}
// Update dashboard
function updateDashboard(healthData, configs, activeConfig) {
const t = translations[currentLanguage];
// System status
const systemStatus = document.getElementById('system-status');
if (healthData.status === 'healthy') {
systemStatus.innerHTML = `<span class="status-indicator status-online"></span>${t.statusOnline}`;
systemStatus.className = 'metric-value success';
} else {
systemStatus.innerHTML = `<span class="status-indicator status-offline"></span>${t.statusOffline}`;
systemStatus.className = 'metric-value danger';
}
// Active provider
if (activeConfig.name) {
document.getElementById('active-provider').textContent = activeConfig.provider;
document.getElementById('active-model').textContent = activeConfig.model_name;
const connectionStatus = document.getElementById('connection-status');
connectionStatus.innerHTML = `<span class="status-indicator status-online"></span>${t.statusOnline}`;
connectionStatus.className = 'metric-value success';
} else {
document.getElementById('active-provider').textContent = 'Nessuno';
document.getElementById('active-model').textContent = 'Nessuno';
const connectionStatus = document.getElementById('connection-status');
connectionStatus.innerHTML = `<span class="status-indicator status-offline"></span>${t.statusOffline}`;
connectionStatus.className = 'metric-value danger';
}
// Configurations count
document.getElementById('total-requests').textContent = configs.length;
// Simulate some metrics (in a real implementation, these would come from the API)
const uptime = Math.floor(Math.random() * 30) + 1;
document.getElementById('system-uptime').textContent = `${uptime} giorni, ${Math.floor(Math.random() * 24)} ore`;
const totalRequests = Math.floor(Math.random() * 1000) + 100;
document.getElementById('total-requests').textContent = totalRequests;
const totalErrors = Math.floor(Math.random() * 10);
document.getElementById('total-errors').textContent = totalErrors;
const errorRate = ((totalErrors / totalRequests) * 100).toFixed(2);
document.getElementById('error-rate').textContent = `${errorRate}%`;
// System resources (simulated)
const cpuUsage = Math.floor(Math.random() * 100);
const memoryUsage = Math.floor(Math.random() * 100);
const diskUsage = Math.floor(Math.random() * 100);
document.getElementById('cpu-usage').textContent = `${cpuUsage}%`;
document.getElementById('memory-usage').textContent = `${memoryUsage}%`;
document.getElementById('disk-usage').textContent = `${diskUsage}%`;
document.getElementById('cpu-bar').style.width = `${cpuUsage}%`;
document.getElementById('memory-bar').style.width = `${memoryUsage}%`;
document.getElementById('disk-bar').style.width = `${diskUsage}%`;
// Tools and plugins
document.getElementById('tools-loaded').textContent = Math.floor(Math.random() * 20) + 5;
document.getElementById('plugins-active').textContent = Math.floor(Math.random() * 5) + 1;
// Performance metrics
const avgResponseTime = (Math.random() * 2).toFixed(3);
document.getElementById('avg-response-time').textContent = `${avgResponseTime}s`;
const requestsPerMinute = Math.floor(Math.random() * 50) + 10;
document.getElementById('requests-per-minute').textContent = requestsPerMinute;
const peakRequests = Math.floor(Math.random() * 100) + 50;
document.getElementById('peak-requests').textContent = peakRequests;
// System info
document.getElementById('system-hostname').textContent = 'langcode-agent';
document.getElementById('system-platform').textContent = navigator.platform;
document.getElementById('system-architecture').textContent = navigator.platform.includes('64') ? 'x64' : 'x86';
document.getElementById('python-version').textContent = '3.11.0';
// Update logs
updateLogs();
}
// Update logs
function updateLogs() {
const logsContainer = document.getElementById('logs-container');
// Simulate log entries
const logTypes = ['info', 'warning', 'error', 'success'];
const logMessages = {
it: [
'Agente inizializzato con successo',
'Configurazione caricata: ollama',
'Test connessione riuscito',
'Strumento eseguito: list_models',
'Avviso: Alto utilizzo memoria',
'Errore: Timeout connessione',
'Plugin caricato: file_tools',
'Richiesta elaborata in 0.245s'
],
en: [
'Agent initialized successfully',
'Configuration loaded: ollama',
'Connection test successful',
'Tool executed: list_models',
'Warning: High memory usage',
'Error: Connection timeout',
'Plugin loaded: file_tools',
'Request processed in 0.245s'
]
};
let logsHtml = '';
for (let i = 0; i < 10; i++) {
const type = logTypes[Math.floor(Math.random() * logTypes.length)];
const messages = logMessages[currentLanguage];
const message = messages[Math.floor(Math.random() * messages.length)];
const timestamp = new Date().toLocaleTimeString();
logsHtml += `<div class="log-entry log-${type}">[${timestamp}] ${message}</div>`;
}
logsContainer.innerHTML = logsHtml;
}
// Update last updated timestamp
function updateLastUpdated() {
const now = new Date();
document.getElementById('last-updated').textContent = now.toLocaleString();
}
// Show alert
function showAlert(message, type) {
const container = document.getElementById('alert-container');
const alert = document.createElement('div');
alert.className = `alert alert-${type}`;
alert.textContent = message;
// Remove existing alerts
container.innerHTML = '';
container.appendChild(alert);
// Auto-remove after 5 seconds
setTimeout(() => {
if (alert.parentNode) {
alert.parentNode.removeChild(alert);
}
}, 5000);
}
</script>
</body>
</html>